Compare commits

..

68 Commits

Author SHA1 Message Date
42d83649be Update authors 2016-07-13 15:59:44 -04:00
df740e3e00 Increment version 2016-07-13 15:57:42 -04:00
c2f43c474b Update .gitignore 2016-07-13 15:57:12 -04:00
893debb361 Fix Windows cross-build 2016-07-13 15:56:26 -04:00
John
0b1ad7415d updated version 2015-06-03 11:26:48 -04:00
John
ba2565dc0e added clean shutdown of LabJack on ctrl-c 2015-06-03 10:54:44 -04:00
35238c57d0 Fix version 2013-08-17 22:21:30 -04:00
e1cb6b6ee0 Bump version 2013-08-09 14:03:02 -04:00
jim
7262486a36 Fix typo in gain config
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@10877 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2012-05-21 19:21:38 +00:00
jim
0f71b79b6f Fix makefile
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@10461 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2012-02-23 18:19:26 +00:00
jdonnal
d6f01a3497 changed nerdjack packet data type for 64 bit compilers and adjusted the Makefile for proper linking of the math library
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@10306 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2012-01-04 03:33:48 +00:00
jim
76143e7eb4 Revert previous commit
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@10233 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2011-11-17 20:01:38 +00:00
jim
3ba616841e Add temperature conversion. Example usage: ethstream -vvv -c -L -C 141 -r 10
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@9728 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2011-06-23 21:22:54 +00:00
jim
6bc683fb21 Support setting values to go with timer modes
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@9689 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2011-06-09 17:58:14 +00:00
jim
640edf6aab Fix windows build
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@9685 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2011-06-06 15:03:45 +00:00
jim
e2bf34ed0c Fix spelling error
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@9684 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2011-06-06 15:03:03 +00:00
cschantz
56a79c00f6 The changes allow ethstream to set the gain of individual labjack analog input channels
with the -g flag and ethstream also grabs the factory calibration constants from the 
Labjack and uses them when the -c flag is given.

changes by Chris Schantz


git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@9683 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2011-06-06 00:39:03 +00:00
nilm
bb4912a43c Add timestamps to debug output in ethstream
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@9487 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2011-03-29 16:08:44 +00:00
nilm
16bf9f89ef ignore SIGPIPE
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@9453 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2011-03-23 19:52:21 +00:00
nilm
267e0e32a5 fix make install when /usr/local/man/man1 does not exist
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@9108 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2011-01-21 03:16:57 +00:00
nilm
588385bcb1 Fix makefile for new ubuntu, no idea why the old one is broken
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@9107 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2011-01-21 03:14:19 +00:00
jim
ced565f372 get rid of extraneous breaks
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@8328 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2010-02-09 07:46:41 +00:00
jim
e6e9d32a54 Don't convert non-analog channels to volts
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@8325 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2010-02-08 23:06:29 +00:00
jim
9cdb4ea77c Fix typo
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@8323 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2010-02-08 22:30:03 +00:00
jim
53ddf346d7 Unify timeouts
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@8321 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2010-02-08 22:13:09 +00:00
jim
b0be6473b8 Fix warnings
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@8320 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2010-02-08 21:37:57 +00:00
jim
9353f2b3f2 ignore that
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@8319 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2010-02-08 21:36:50 +00:00
jim
b697801e3e Don't store this
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@8318 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2010-02-08 21:36:19 +00:00
jim
5a14055029 Fix some build stuff with dependency tracking (oops)
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@8317 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2010-02-08 21:35:23 +00:00
jim
25bed4b644 rename
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@8316 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2010-02-08 21:25:26 +00:00
jim
cd63e2fb11 Move ethstream to the parent directory
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@8314 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2010-02-08 21:23:12 +00:00
zacharyc
329871bf56 Fixed example formatting a bit.
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7464 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-04-23 16:26:24 +00:00
zacharyc
f4f809eee3 Added "examples" to code
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7463 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-04-23 16:20:07 +00:00
zacharyc
ec1ae04a5b Bumped version number and finished fixing up Windows autodetect support.
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7454 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-04-17 17:06:26 +00:00
zacharyc
67163dc1a2 Added Windows stub support for autodetection.
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7453 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-04-17 16:14:36 +00:00
zacharyc
a29bf180ab Implemented Jim's hdhomerun autodetect for POSIX. Works great.
Windows testing next.


git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7452 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-04-17 15:57:42 +00:00
zacharyc
4cccb783f0 Fixed up "range" argument
Took Jim's suggestions about addressing and device options
Made autodetection a better fallback.


git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7450 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-04-16 23:12:15 +00:00
nilm
5e7f4ac97e Increased timeout to at least 1 minute
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7433 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-04-14 15:49:53 +00:00
zacharyc
a57c0c22c2 Massive cleanup and refactoring of data_stream function.
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7431 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-04-11 17:35:54 +00:00
zacharyc
2e3f2f4b23 Made ethstream reset a little easier.
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7426 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-04-10 20:35:36 +00:00
zacharyc
4592b6a75b Changed nerdjack limits
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7423 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-04-10 17:17:39 +00:00
nilm
d1447e2af8 Changed clock again
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7421 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-04-10 15:58:33 +00:00
nilm
b9130788f2 Fixed annoying bug with NerdJack header
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7419 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-04-09 22:00:15 +00:00
nilm
ed150ead35 Changed around packet structure
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7416 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-04-08 15:37:07 +00:00
nilm
519a0c2e29 Made changes to ethstream to work with new clock
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7415 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-04-07 15:38:19 +00:00
zacharyc
190fd3f3a7 Reverted clock change.
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7406 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-04-03 17:44:57 +00:00
zacharyc
9c174d12b0 Forgot a carriage return
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7404 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-04-03 17:04:29 +00:00
zacharyc
b1e049eed0 Changed clock rate of NerdJack and added error checking to packets
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7403 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-04-03 17:03:00 +00:00
zacharyc
d461365275 Added better error recovery code.
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7314 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-03-19 22:19:05 +00:00
zacharyc
a4eede145b Added limits to issue warnings about high sample rates
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7303 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-03-18 22:36:31 +00:00
zacharyc
6562c0b787 Fixed bug when output pipe is closed without a KILL signal
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7301 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-03-18 17:27:46 +00:00
zacharyc
7f79ec8ff3 Ran indent on code to fix inconsistent style.
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7298 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-03-18 15:23:10 +00:00
zacharyc
1dd09ea52d Made timeouts more reasonable for small sample rates.
Fixed error in period calculation's interaction with the PWM bug


git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7297 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-03-18 15:20:41 +00:00
zacharyc
ded6c7e5f4 Removed silicon workaround
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7289 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-03-17 21:32:50 +00:00
zacharyc
f2d6566051 Packet reordering shifted PC side.
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7287 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-03-17 21:07:18 +00:00
zacharyc
357e808986 Introduced usage of full 20 bits of PWM period counter
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7286 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-03-17 20:25:49 +00:00
zacharyc
0ed2935deb Better command structure using hton, ntoh, and friends
Altered command structure to support full 20 bit period


git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7282 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-03-13 20:33:52 +00:00
zacharyc
1a2ed51d0c Added one second sleep in code to let nerdjack recover from previous run
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7274 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-03-12 21:50:37 +00:00
zacharyc
679b4ef64f Fixed a few bugs to increase reliability and drop first line.
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7262 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-03-09 21:36:59 +00:00
zacharyc
770a83b8f7 Fixed bug in precision argument for ethstream
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7260 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-03-09 18:12:09 +00:00
zacharyc
627a1bf22b Slight bit of code cleanup. More to come
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7254 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-03-06 17:20:49 +00:00
zacharyc
43c8e19c67 Basic resume functionality working
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7250 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-03-05 21:43:25 +00:00
zacharyc
4aab606162 Fixed up rate determination and added memory test routine for NerdJack
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7235 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-03-02 16:35:54 +00:00
zacharyc
c81d372d23 Made commands fixed width
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7220 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-02-24 14:50:11 +00:00
zacharyc
407a91c764 Merged Jim's changes to ljstream. This now should respect disk full errors
and allow hex output


git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7214 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-02-23 15:47:55 +00:00
zacharyc
1d760e033b Updated ethstream to have separate Command and Data ports for NerdJack
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7212 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-02-23 14:39:26 +00:00
zacharyc
57cfac2bec Changed output format so that numbers are between 0 and 65536 instead of
signed output. This matches LabJack behavior


git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7141 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-02-02 20:09:01 +00:00
zacharyc
5b731f3e7e Now ethstream falls back from Labjack to Nerdjack
git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7140 ddd99763-3ecb-0310-9145-efcb8ce7c51f
2009-02-02 18:29:05 +00:00
22 changed files with 1798 additions and 876 deletions

10
.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
ethstream
ethstream.1
ethstream.txt
ethstream.exe
*.obj
*.d
*.dobj
*.o

View File

@ -14,14 +14,17 @@
# 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
#WINCC = i386-mingw32-gcc
WINCC = i586-mingw32msvc-gcc
WINCFLAGS += $(CFLAGS)
WINLDFLAGS += $(LDFLAGS) -lws2_32 -s
WINLDFLAGS += $(LDFLAGS) -lws2_32 -liphlpapi -s
# Targets
@ -32,11 +35,10 @@ default: lin
all: lin win
.PHONY: lin
lin: ljtest ethstream ljconfig \
ethstream.1 ljconfig.1
lin: ethstream ethstream.1 ethstream.txt
.PHONY: win
win: ljtest.exe ethstream.exe ljconfig.exe
win: ethstream.exe
version.h: VERSION
@ -46,17 +48,12 @@ version.h: VERSION
# 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)
$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
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
@ -71,6 +68,7 @@ ljtest.exe: $(obj-ljtest:.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}
@ -92,16 +90,19 @@ dist: version.h
.PHONY: clean distclean
clean distclean:
rm -f *.o *.obj *.exe ethstream ljtest ljconfig core *.d *.1 *.txt
rm -f *.o *.obj *.exe ethstream core *.d *.dobj *.1 *.txt
# Dependency tracking:
allsources = $(wildcard *.c)
-include $(allsources:.c=.d)
%.o : %.c
$(COMPILE.c) -MP -MMD -MT '$*.obj' -o $@ $<
$(COMPILE.c) -MP -MMD -MT '$*.o' -MF '$*.d' -o $@ $<
-include $(allsources:.c=.dobj)
%.obj : %.c
$(WINCC) $(WINCFLAGS) -MP -MMD -MT '$*.o' -c -o $@ $<
$(WINCC) $(WINCFLAGS) -MP -MMD -MT '$*.obj' -MF '$*.dobj' -c -o $@ $<
# Win32 executable

11
README
View File

@ -1,6 +1,13 @@
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:
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
The NerdJack device is a custom board made in LEES by Zach Clifford.
Use ethstream -h or ethstream -X for usage instructions and examples.

View File

@ -1 +1 @@
1.0
1.3.2

View File

@ -2,6 +2,7 @@
#include <stdio.h>
#include "compat.h"
#include <windows.h>
#include "errno.h"
unsigned int sleep(unsigned int seconds)
{
@ -14,36 +15,37 @@ static struct {
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 },
{ 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;
@ -80,4 +82,3 @@ char *compat_strerror(int errnum)
}
*/
#endif

View File

@ -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

19
debug.c
View File

@ -1,14 +1,18 @@
#include "debug.h"
#include <stdio.h>
#include <stdarg.h>
#include <sys/time.h>
int verb_count = 0;
int func_fprintf(const char *func, FILE *stream, const char *format, ...)
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);
@ -16,3 +20,16 @@ int func_fprintf(const char *func, FILE *stream, const char *format, ...)
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;
}

11
debug.h
View File

@ -14,8 +14,10 @@ extern int verb_count;
#include <stdio.h>
int func_fprintf(const char *func, FILE *stream, const char *format,
...) __attribute__ ((format (printf, 3, 4)));
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); \
})

View File

@ -7,11 +7,6 @@
* 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>
@ -29,47 +24,91 @@
#include "opt.h"
#include "version.h"
#include "compat.h"
#include "ethstream.h"
#include "example.inc"
#define DEFAULT_HOST "192.168.1.209"
#define UE9_COMMAND_PORT 52360
#define UE9_DATA_PORT 52361
struct callbackInfo {
#define MAX_CHANNELS 256
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 }
{'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", NULL, "Force LabJack device"},
{'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/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"},
{'h', "help", NULL, "this help"},
{'v', "verbose", NULL, "be verbose"},
{'V', "version", NULL, "show version number and exit"},
{'i', "info", NULL, "get info from device (NJ only)"},
{'X', "examples", NULL, "show ethstream examples and exit"},
{0, NULL, NULL, NULL}
};
int doStream(const char *address, uint8_t scanconfig, uint16_t scaninterval,
int *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 *channel_list, int channel_count,
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, 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);
}
@ -87,19 +126,26 @@ int main(int argc, char *argv[])
double actual_rate;
int oneshot = 0;
int forceretry = 0;
int convert = 0;
int convert = CONVERT_DEC;
int showmem = 0;
int inform = 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 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;
int detect = 0;
int precision = 0;
int period = NERDJACK_CLOCK_RATE / desired_rate;
int nerdjack = 0;
int labjack = 0;
int detect = 0;
int precision = 0;
int addressSpecified = 0;
int donerdjack = 0;
unsigned long period = NERDJACK_CLOCK_RATE / desired_rate;
/* Parse arguments */
opt_init(&optind);
@ -108,11 +154,12 @@ int main(int argc, char *argv[])
case 'a':
free(address);
address = strdup(optarg);
addressSpecified = 1;
break;
case 'n':
channel_count = 0;
tmp = strtol(optarg, &endp, 0);
if (*endp || tmp < 1 || tmp > UE9_CHANNELS) {
if (*endp || tmp < 1 || tmp > MAX_CHANNELS) {
info("bad number of channels: %s\n", optarg);
goto printhelp;
}
@ -123,29 +170,90 @@ int main(int argc, char *argv[])
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);
if (*endp != '\0' && *endp != ',') {
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");
//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 (channel_count >= MAX_CHANNELS) {
info("error: too many channels specified\n");
goto printhelp;
}
channel_list[channel_count++] = tmp;
optarg = endp + 1;
} while (*endp);
break;
}
while (*endp);
break;
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;
}
if (timer_mode_count >= UE9_TIMERS) {
info("error: too many timers specified\n");
goto printhelp;
}
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);
break;
case 'T': /* labjack only */
timer_divisor = strtod(optarg, &endp);
if (*endp || timer_divisor < 0 || timer_divisor > 255) {
info("bad timer divisor: %s\n", optarg);
goto printhelp;
}
break;
case 'r':
desired_rate = strtod(optarg, &endp);
if(*endp || desired_rate <= 0) {
if (*endp || desired_rate <= 0) {
info("bad rate: %s\n", optarg);
goto printhelp;
}
@ -157,38 +265,90 @@ int main(int argc, char *argv[])
goto printhelp;
}
break;
case 'p':
precision++;
break;
case 'N':
nerdjack++;
break;
case 'd':
detect++;
break;
case 'R':
tmp = strtol(optarg, &endp, 0);
if (*endp != ',') {
info("bad range number: %s\n", optarg);
goto printhelp;
}
if (tmp != 5 && tmp != 10) {
info("valid choices for range are 5 or 10\n");
goto printhelp;
}
if (tmp == 5)
precision = precision + 1;
optarg = endp + 1;
if (*endp == '\0') {
info("Range needs two numbers, one for channels 0-5 and another for 6-11\n");
goto printhelp;
}
tmp = strtol(optarg, &endp, 0);
if (*endp != '\0') {
info("Range needs only two numbers, one for channels 0-5 and another for 6-11\n");
goto printhelp;
}
if (tmp != 5 && tmp != 10) {
info("valid choices for range are 5 or 10\n");
goto printhelp;
}
if (tmp == 5)
precision = precision + 2;
break;
case 'N':
nerdjack++;
break;
case 'L':
labjack++;
break;
case 'd':
detect++;
break;
case 'o':
oneshot++;
break;
case 'f':
forceretry++;
forceretry++;
break;
case 'c':
convert++;
if (convert != 0) {
info("specify only one conversion type\n");
goto printhelp;
}
convert = CONVERT_VOLTS;
break;
case 'H':
if (convert != 0) {
info("specify only one conversion type\n");
goto printhelp;
}
convert = CONVERT_HEX;
break;
case 'm':
showmem++;
case 'v':
verb_count++;
break;
case 'X':
printf("%s", examplestring);
return 0;
break;
case 'V':
printf("ljstream " 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");
return 0;
break;
case 'i':
inform++;
break;
case 'h':
help = stdout;
default:
printhelp:
printhelp:
fprintf(help, "Usage: %s [options]\n", *argv);
opt_help(opt, help);
fprintf(help, "Read data from the specified Labjack UE9"
@ -196,32 +356,99 @@ int main(int argc, char *argv[])
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 (detect && labjack) {
info("The LabJack does not support autodetection\n");
goto printhelp;
}
if (detect && !nerdjack) {
info("Only the NerdJack supports autodetection - assuming -N option\n");
nerdjack = 1;
}
if (detect && addressSpecified) {
info("Autodetection and specifying address are mutually exclusive\n");
goto printhelp;
}
if (nerdjack && labjack) {
info("Nerdjack and Labjack options are mutually exclusive\n");
goto printhelp;
}
donerdjack = nerdjack;
//First if no options were supplied try the Nerdjack
//The second time through, donerdjack will be true and this will not fire
if (!nerdjack && !labjack) {
info("No device specified...Defaulting to Nerdjack\n");
donerdjack = 1;
}
doneparse:
if (inform) {
//We just want information from NerdJack
if (!detect) {
if (nerd_get_version(address) < 0) {
info("Could not find NerdJack at specified address\n");
} else {
return 0;
}
}
info("Autodetecting NerdJack address\n");
free(address);
if (nerdjack_detect(address) < 0) {
info("Error with autodetection\n");
goto printhelp;
} else {
info("Found NerdJack at address: %s\n", address);
if (nerd_get_version(address) < 0) {
info("Error getting NerdJack version\n");
goto printhelp;
}
return 0;
}
}
if (donerdjack) {
if (channel_count > NERDJACK_CHANNELS) {
info("Too many channels for NerdJack\n");
goto printhelp;
}
for (i = 0; i < channel_count; i++) {
if (channel_list[i] >= NERDJACK_CHANNELS) {
info("Channel is out of NerdJack range: %d\n",
channel_list[i]);
goto printhelp;
}
}
} else {
if (channel_count > UE9_MAX_CHANNEL_COUNT) {
info("Too many channels for LabJack\n");
goto printhelp;
}
for (i = 0; i < channel_count; i++) {
if (channel_list[i] > UE9_MAX_CHANNEL) {
info("Channel is out of LabJack range: %d\n",
channel_list[i]);
goto printhelp;
}
}
}
/* Timer requires Labjack */
if (timer_mode_count && !labjack) {
info("Can't use timers on NerdJack\n");
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]);
@ -232,7 +459,7 @@ int main(int argc, char *argv[])
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;
@ -242,29 +469,27 @@ 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. */
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 (donerdjack) {
if (nerdjack_choose_scan(desired_rate, &actual_rate, &period) <
0) {
info("error: can't achieve requested scan rate (%lf Hz)\n", desired_rate);
}
} else {
if (ue9_choose_scan(desired_rate, &actual_rate,
&scanconfig, &scaninterval) < 0) {
info("error: can't achieve requested scan rate (%lf Hz)\n", desired_rate);
}
}
if ((desired_rate != actual_rate) || verb_count)
if ((desired_rate != actual_rate) || verb_count) {
info("Actual scanrate is %lf Hz\n", actual_rate);
info("Period is %ld\n", period);
}
if (verb_count && lines) {
info("Stopping capture after %d lines\n", lines);
@ -272,35 +497,70 @@ int main(int argc, char *argv[])
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);
}
}
#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);
if (nerdjack_detect(address) < 0) {
info("Error with autodetection\n");
goto printhelp;
} 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 (donerdjack) {
ret =
nerdDoStream(address, channel_list, channel_count,
precision, period, convert, lines,
showmem);
verb("nerdDoStream returned %d\n", ret);
} else {
ret = doStream(address, scanconfig, scaninterval,
channel_list, channel_count,
timer_mode_list, timer_value_list,
timer_mode_count, timer_divisor,
gain_list, gain_count,
convert, lines);
verb("doStream returned %d\n", ret);
}
if (oneshot)
break;
if (ret == 0)
break;
break;
//Neither options specified at command line and first time through.
//Try LabJack
if (ret == -ENOTCONN && donerdjack && !labjack && !nerdjack) {
info("Could not connect NerdJack...Trying LabJack\n");
donerdjack = 0;
goto doneparse;
}
//Neither option supplied, no address, and second time through.
//Try autodetection
if (ret == -ENOTCONN && !donerdjack && !labjack && !nerdjack
&& !addressSpecified) {
info("Could not connect LabJack...Trying to autodetect Nerdjack\n");
detect = 1;
donerdjack = 1;
goto doneparse;
}
if (ret == -ENOTCONN && nerdjack && !detect
&& !addressSpecified) {
info("Could not reach NerdJack...Trying to autodetect\n");
detect = 1;
goto doneparse;
}
if (ret == -ENOTCONN && !forceretry) {
info("Initial connection failed, giving up\n");
@ -321,34 +581,87 @@ int main(int argc, char *argv[])
return 0;
}
int nerdDoStream(const char *address, int *channel_list, int channel_count, int precision,
unsigned short period, int convert, int lines)
int
nerdDoStream(const char *address, int *channel_list, int channel_count,
int precision, unsigned long period, int convert, int lines,
int showmem)
{
int retval = -EAGAIN;
int fd_data;
static int first_call = 1;
char command[13];
static int started = 0;
static int wasreset = 0;
getPacket command;
static unsigned short currentcount = 0;
tryagain:
/* Open connection. If this fails, and this is the
first attempt, return a different error code so we give up. */
//If this is the first time, set up acquisition
//Otherwise try to resume the previous one
if (started == 0) {
if (nerd_generate_command
(&command, channel_list, channel_count, precision,
period) < 0) {
info("Failed to create configuration command\n");
goto out;
}
if (nerd_send_command(address, "STOP", 4) < 0) {
if (first_call) {
retval = -ENOTCONN;
if (verb_count)
info("Failed to send STOP command\n");
} else {
info("Failed to send STOP command\n");
}
goto out;
}
if (nerd_send_command(address, &command, sizeof(command)) < 0) {
info("Failed to send GET command\n");
goto out;
}
} else {
//If we had a transmission in progress, send a command to resume from there
char cmdbuf[10];
sprintf(cmdbuf, "SETC%05hd", currentcount);
retval = nerd_send_command(address, cmdbuf, strlen(cmdbuf));
if (retval == -4) {
info("NerdJack was reset\n");
//Assume we have not started yet, reset on this side.
//If this routine is retried, start over
printf("# NerdJack was reset here\n");
currentcount = 0;
started = 0;
wasreset = 1;
goto tryagain;
} else if (retval < 0) {
info("Failed to send SETC command\n");
goto out;
}
}
//The transmission has begun
started = 1;
/* Open connection */
fd_data = nerd_open(address, NERDJACK_DATA_PORT);
if (fd_data < 0) {
info("Connect failed: %s:%d\n", address, NERDJACK_DATA_PORT);
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;
}
retval = nerd_data_stream
(fd_data, channel_count, channel_list, precision, convert, lines,
showmem, &currentcount, period, wasreset);
wasreset = 0;
if (retval == -3) {
retval = 0;
}
if (retval < 0) {
info("Failed to open data stream\n");
goto out1;
}
info("Stream finished\n");
retval = 0;
@ -356,15 +669,22 @@ int nerdDoStream(const char *address, int *channel_list, int channel_count, int
out1:
nerd_close_conn(fd_data);
out:
//We've tried communicating, so this is not the first call anymore
first_call = 0;
return retval;
}
int doStream(const char *address, uint8_t scanconfig, uint16_t scaninterval,
int *channel_list, int channel_count, int convert, int lines)
int
doStream(const char *address, uint8_t scanconfig, uint16_t scaninterval,
int *channel_list, int channel_count,
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 ret;
// int fd_cmd, fd_data; *these are now globals so sighandler can use them*
int ret;
static int first_call = 1;
struct callbackInfo ci = {
.convert = convert,
@ -372,7 +692,7 @@ int doStream(const char *address, uint8_t scanconfig, uint16_t scaninterval,
};
/* Open command connection. If this fails, and this is the
first attempt, return a different error code so we give up. */
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);
@ -393,19 +713,37 @@ int doStream(const char *address, uint8_t scanconfig, uint16_t scaninterval,
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");
/* Set timer configuration */
if (timer_mode_count &&
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;
}
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 */
@ -415,7 +753,9 @@ int doStream(const char *address, uint8_t scanconfig, uint16_t scaninterval,
}
/* Stream data */
ret = ue9_stream_data(fd_data, channel_count, data_callback, (void *)&ci);
ue9_running = 1;
ret =
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;
@ -433,10 +773,11 @@ int 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;
@ -444,22 +785,52 @@ int data_callback(int channels, uint16_t *data, void *context)
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]);
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;
} else if (ci->convert == CONVERT_HEX) {
/* CONVERT_HEX */
if (printf("%04X", data[i]) < 0)
goto bad;
} else {
/* CONVERT_DEC */
if (printf("%d", data[i]) < 0)
goto bad;
}
columns_left--;
if (i < (channels - 1)) {
putchar(' ');
if (ci->convert != CONVERT_HEX && putchar(' ') < 0)
goto bad;
} else {
putchar('\n');
if (putchar('\n') < 0)
goto bad;
lines++;
if (ci->maxlines && lines >= ci->maxlines)
return -1;
}
}
return 0;
bad:
info("Output error (disk full?)\n");
return -3;
}

10
ethstream.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef ETHSTREAM_H
#define ETHSTREAM_H
#define CONVERT_DEC 0
#define CONVERT_VOLTS 1
#define CONVERT_HEX 2
#define TIMEOUT 5 /* Timeout for connect/send/recv, in seconds */
#endif

106
example.inc Normal file
View File

@ -0,0 +1,106 @@
char examplestring[] = "\n\
\n\
Welcome to the NILM Ethstream examples.\n\
\n\
For the most part, typing \"ethstream\" by itself will sample the first\n\
two channels at 8 kHz on 10V range. Press CTRL-C to terminate sampling.\n\
\n\
If you want current measurements on the first two phases of NILM\n\
with default sample rate of 8 kHz and 10V range:\n\
\n\
ethstream -C 0,3\n\
\n\
The device is configured so that channels 0 through 2 are currents for\n\
the three phases and channels 3-5 are for voltages of the three phases.\n\
The current channels sample voltages that will depend on the DIP switch\n\
settings in the NILM box. The DIP switch positions allow you to convert\n\
ethstream's readings to true current readings.\n\
\n\
If you want only currents at 16 kHz and 10V range:\n\
\n\
ethstream -n 3 -r 16000\n\
\n\
The -n option samples a number of channels starting at 0. The rate can be\n\
at least 16000 if 12 channels are sampled , but it can do more if\n\
fewer channels are sampled. The limiting factor is the highest channel\n\
sampled. Sampling just the top channel (11) is as bad as sampling\n\
all 12 at once.\n\
Ethstream will warn if you approach the limits of the NerdJack with the\n\
given sampled channels. Sampling outside the range of the NerdJack might\n\
result in corrupt data or crashing of the device. There will be no\n\
permanent damage to NILM or NerdJack, but be aware of the possibility of\n\
data corruption.\n\
\n\
If you need a higher accuracy but lower range measurement on the voltages:\n\
\n\
ethstream -R 5,10 -C 3,4,5\n\
\n\
The two numbers to the R command set the range to either 5V or 10V. Above,\n\
we are setting channels 0-5 to 5 V range and channels 6-11 to 10 V range.\n\
Channels 6-11 are unconnected, but they can have range set independently.\n\
\n\
All of the above examples output a digital number from 0 to 65535 with\n\
65535 representing the highest range (5V or 10V). 0 represents the most\n\
negative range (-5V or -10V). If you want conversion\n\
to volts for all six voltages and currents:\n\
\n\
ethstream -c -C 0,3,1,4,2,5\n\
\n\
The channels will be output in the order given in the C command. This\n\
command will group the current and voltage data by phase.\n\
\n\
If you are supplying data from ethstream to another program, you might\n\
want to dump its output to a file and terminate after a certain number of\n\
samples:\n\
\n\
ethstream -n 6 -r 8000 - l 16000 > outfile.dat\n\
\n\
This will take 16000 samples at 8 kHz (2 seconds of acquisition) of all\n\
channels and write the data to outfile.dat. This can be directly read\n\
by a package like MATLAB.\n\
\n\
If there are multiple NerdJacks or you have changed the TCP/IP settings\n\
from default, you might have to specify which one you want to talk to:\n\
\n\
ethstream -a 192.168.1.210\n\
\n\
This will sample two channels at 8 kHz from the NerdJack at 192.168.1.210.\n\
This is the default \"1\" setting on the NerdJack. If no address is\n\
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\
";

View File

@ -1,98 +0,0 @@
/*
* 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;
}

View File

@ -1,67 +0,0 @@
/*
* 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;
}

View File

@ -13,7 +13,7 @@
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <math.h>
#include "netutil.h"
@ -23,336 +23,670 @@
#include "nerdjack.h"
#include "util.h"
#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
typedef struct __attribute__ ((__packed__)) {
unsigned char headerone;
unsigned char headertwo;
unsigned short packetNumber;
unsigned short adcused;
unsigned short packetsready;
signed short data[NERDJACK_NUM_SAMPLES];
} dataPacket;
struct discovered_socket {
int sock;
uint32_t local_ip;
uint32_t subnet_mask;
};
struct discover_t {
struct discovered_socket socks[MAX_SOCKETS];
unsigned int sock_count;
};
/* 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)
int
nerdjack_choose_scan(double desired_rate, double *actual_rate,
unsigned long *period)
{
*period = round((double) NERDJACK_CLOCK_RATE / desired_rate);
* actual_rate = (double) NERDJACK_CLOCK_RATE / (double) *period;
if(*actual_rate != desired_rate) {
//The ffffe is because of a silicon bug. The last bit is unusable in all
//devices so far. It is worked around on the chip, but giving it exactly
//0xfffff would cause the workaround code to roll over.
*period = floor((double)NERDJACK_CLOCK_RATE / desired_rate);
if (*period > 0x0ffffe) {
info("Cannot sample that slowly\n");
*actual_rate = (double)NERDJACK_CLOCK_RATE / (double)0x0ffffe;
*period = 0x0ffffe;
return -1;
}
return 0;
//Period holds the period register for the NerdJack, so it needs to be right
*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;
/**
* Create a discovered socket and add it to the socket list structure.
* All sockets in the structure should be created, bound, and ready for broadcasting
*/
static int discovered_sock_create(struct discover_t *ds, uint32_t local_ip,
uint32_t subnet_mask)
{
if (ds->sock_count >= MAX_SOCKETS) {
return 0;
}
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));
/* Create socket. */
int sock = (int)socket(AF_INET, SOCK_DGRAM, 0);
if (sock == -1) {
return 0;
}
close(sock); /* close the socket */
close(receivesock);
return 0;
/* Allow broadcast. */
int sock_opt = 1;
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&sock_opt,
sizeof(sock_opt));
/* Set nonblocking */
if (soblock(sock, 0) < 0) {
verb("can't set nonblocking\n");
close(sock);
return 0;
}
/* Bind socket. */
struct sockaddr_in sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
sock_addr.sin_addr.s_addr = htonl(local_ip);
sock_addr.sin_port = htons(0);
if (bind(sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
close(sock);
return 0;
}
/* Write sock entry. */
struct discovered_socket *dss = &ds->socks[ds->sock_count++];
dss->sock = sock;
dss->local_ip = local_ip;
dss->subnet_mask = subnet_mask;
return 1;
}
typedef struct {
int numCopies;
int * destlist;
} deststruct;
int nerd_data_stream(int data_fd, char * command, int numChannels, int *channel_list, int precision, int convert, int lines)
/**
* Enumerate all interfaces we can find and open sockets on each
*/
#if defined(USE_IPHLPAPI)
static void enumerate_interfaces(struct discover_t *ds)
{
unsigned char buf[NERDJACK_PACKET_SIZE];
PIP_ADAPTER_INFO pAdapterInfo =
(IP_ADAPTER_INFO *) malloc(sizeof(IP_ADAPTER_INFO));
ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
//int numGroups = NERDJACK_NUM_SAMPLES / numChannels;
DWORD Ret = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
if (Ret != NO_ERROR) {
free(pAdapterInfo);
if (Ret != ERROR_BUFFER_OVERFLOW) {
return;
}
pAdapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen);
Ret = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
if (Ret != NO_ERROR) {
free(pAdapterInfo);
return;
}
}
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];
deststruct destination[NERDJACK_CHANNELS];
int tempdestlist[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;
PIP_ADAPTER_INFO pAdapter = pAdapterInfo;
while (pAdapter) {
IP_ADDR_STRING *pIPAddr = &pAdapter->IpAddressList;
while (pIPAddr) {
uint32_t local_ip =
ntohl(inet_addr(pIPAddr->IpAddress.String));
uint32_t mask =
ntohl(inet_addr(pIPAddr->IpMask.String));
int numgroups = 0;
long double volts;
int channels_left = numChannels;
int channelprocessing = 0;
int currentalign = 0; //Index into sampled channels
int i;
int numDuplicates = 0;
//Loop through channel_list until all channels recognized
//start with channelprocessing = 0 and increment through channels.
//If a channel is found in the list set it up appropriately.
do {
//numduplicates = 0;
destination[currentalign].numCopies = 0;
for(i = 0; i < numChannels; i++) {
if(channelprocessing == channel_list[i]) {
//destination[currentalign] = i;
tempdestlist[destination[currentalign].numCopies] = i;
if(destination[currentalign].numCopies > 0) {
numDuplicates++;
}
destination[currentalign].numCopies++;
//currentalign++;
channels_left--;
//break;
}
}
if(destination[currentalign].numCopies > 0) {
destination[currentalign].destlist = malloc( destination[currentalign].numCopies * sizeof(int) );
memcpy(destination[currentalign].destlist, tempdestlist, destination[currentalign].numCopies * sizeof(int));
currentalign++;
}
channelprocessing++;
} while(channels_left > 0);
int numChannelsSampled = numChannels - numDuplicates;
int numGroups = NERDJACK_NUM_SAMPLES / numChannelsSampled;
if (local_ip == 0) {
pIPAddr = pIPAddr->Next;
continue;
}
/* Send request */
ret = send_all_timeout(data_fd, command, strlen(command), 0,
& (struct timeval) { .tv_sec = NERDJACK_TIMEOUT });
if (ret < 0 || ret != strlen(command)) {
discovered_sock_create(ds, local_ip, mask);
pIPAddr = pIPAddr->Next;
}
pAdapter = pAdapter->Next;
}
free(pAdapterInfo);
}
#else
static void enumerate_interfaces(struct discover_t *ds)
{
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd == -1) {
return;
}
struct ifconf ifc;
uint8_t buf[8192];
ifc.ifc_len = sizeof(buf);
ifc.ifc_buf = (char *)buf;
memset(buf, 0, sizeof(buf));
if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
close(fd);
return;
}
uint8_t *ptr = (uint8_t *) ifc.ifc_req;
uint8_t *end = (uint8_t *) & ifc.ifc_buf[ifc.ifc_len];
while (ptr <= end) {
struct ifreq *ifr = (struct ifreq *)ptr;
ptr += _SIZEOF_ADDR_IFREQ(*ifr);
if (ioctl(fd, SIOCGIFADDR, ifr) != 0) {
continue;
}
struct sockaddr_in *addr_in =
(struct sockaddr_in *)&(ifr->ifr_addr);
uint32_t local_ip = ntohl(addr_in->sin_addr.s_addr);
if (local_ip == 0) {
continue;
}
if (ioctl(fd, SIOCGIFNETMASK, ifr) != 0) {
continue;
}
struct sockaddr_in *mask_in =
(struct sockaddr_in *)&(ifr->ifr_addr);
uint32_t mask = ntohl(mask_in->sin_addr.s_addr);
discovered_sock_create(ds, local_ip, mask);
}
}
#endif
/**
* Close all sockets previously enumerated and free the struct
*/
static void destroy_socks(struct discover_t *ds)
{
unsigned int i;
for (i = 0; i < ds->sock_count; i++) {
struct discovered_socket *dss = &ds->socks[i];
close(dss->sock);
}
free(ds);
}
/* Perform autodetection. Returns 0 on success, -1 on error
* Sets ipAddress to the detected address
*/
int nerdjack_detect(char *ipAddress)
{
int32_t receivesock;
struct sockaddr_in sa, receiveaddr, sFromAddr;
int buffer_length;
char buffer[200];
char incomingData[10];
socklen_t lFromLen;
sprintf(buffer, "TEST");
buffer_length = strlen(buffer) + 1;
net_init();
receivesock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
/* Set nonblocking */
if (soblock(receivesock, 0) < 0) {
verb("can't set nonblocking\n");
return -1;
}
if (-1 == receivesock) { /* if socket failed to initialize, exit */
verb("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
receiveaddr.sin_addr.s_addr = INADDR_ANY;
bind(receivesock, (struct sockaddr *)&receiveaddr,
sizeof(struct sockaddr_in));
struct discover_t *ds =
(struct discover_t *)calloc(1, sizeof(struct discover_t));
if (!ds) {
return -1;
}
/* Create a routable broadcast socket. */
if (!discovered_sock_create(ds, 0, 0)) {
free(ds);
return -1;
}
/* Detect & create local sockets. */
enumerate_interfaces(ds);
/*
* Send subnet broadcast using each local ip socket.
* This will work with multiple separate 169.254.x.x interfaces.
*/
unsigned int i;
for (i = 0; i < ds->sock_count; i++) {
struct discovered_socket *dss = &ds->socks[i];
uint32_t target_ip = dss->local_ip | ~dss->subnet_mask;
sa.sin_addr.s_addr = htonl(target_ip);
sendto(dss->sock, buffer, buffer_length, 0,
(struct sockaddr *)&sa, sizeof(struct sockaddr_in));
}
destroy_socks(ds);
lFromLen = sizeof(sFromAddr);
if (0 >
recvfrom_timeout(receivesock, incomingData, sizeof(incomingData), 0,
(struct sockaddr *)&sFromAddr, &lFromLen,
&(struct timeval) {
.tv_sec = TIMEOUT})) {
close(receivesock);
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(receivesock);
return 0;
}
/*
* Get the NerdJack version string and print it
*/
int nerd_get_version(const char *address)
{
int ret, fd_command;
char buf[200];
fd_command = nerd_open(address, NERDJACK_COMMAND_PORT);
if (fd_command < 0) {
info("Connect failed: %s:%d\n", address, NERDJACK_COMMAND_PORT);
return -2;
}
/* Send request */
ret = send_all_timeout(fd_command, "VERS", 4, 0, &(struct timeval) {
.tv_sec = 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 = TIMEOUT});
nerd_close_conn(fd_command);
if (ret < 0) {
verb("Error receiving command\n");
return -1;
}
//Slice off the "OK" from the string
buf[strlen(buf) - 2] = '\0';
printf("%s\n", buf);
return 0;
}
/* Send the given command to address. The command should be something
* of the specified length. This expects the NerdJack to reply with OK
* or NO
*/
int nerd_send_command(const char *address, void *command, int length)
{
int ret, fd_command;
char buf[3];
fd_command = nerd_open(address, NERDJACK_COMMAND_PORT);
if (fd_command < 0) {
info("Connect failed: %s:%d\n", address, NERDJACK_COMMAND_PORT);
return -2;
}
/* Send request */
ret = send_all_timeout(fd_command, command, length, 0, &(struct timeval) {
.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 = TIMEOUT});
nerd_close_conn(fd_command);
if (ret < 0 || ret != 3) {
verb("Error receiving OK for command\n");
return -1;
}
if (0 != strcmp("OK", buf)) {
verb("Did not receive OK. Received %s\n", buf);
return -4;
}
return 0;
}
int
nerd_data_stream(int data_fd, int numChannels, int *channel_list,
int precision, int convert, int lines, int showmem,
unsigned short *currentcount, unsigned int period,
int wasreset)
{
//Variables that should persist across retries
static dataPacket buf;
static int linesleft = 0;
static int linesdumped = 0;
//Variables essential to packet processing
signed short datapoint = 0;
int i;
int numChannelsSampled = channel_list[0] + 1;
//The number sampled will be the highest channel requested plus 1
//(i.e. channel 0 requested means 1 sampled)
for (i = 0; i < numChannels; i++) {
if (channel_list[i] + 1 > numChannelsSampled)
numChannelsSampled = channel_list[i] + 1;
}
double voltline[numChannels];
unsigned short dataline[numChannels];
unsigned short packetsready = 0;
unsigned short adcused = 0;
unsigned short tempshort = 0;
int charsread = 0;
int numgroupsProcessed = 0;
double volts;
//The timeout should be the expected time plus 60 seconds
//This permits slower speeds to work properly
unsigned int expectedtimeout =
(period * NERDJACK_NUM_SAMPLES / NERDJACK_CLOCK_RATE) + 60;
//Check to see if we're trying to resume
//Don't blow away linesleft in that case
if (lines != 0 && linesleft == 0) {
linesleft = lines;
}
//If there was a reset, we still need to dump a line because of faulty PDCA start
if (wasreset) {
linesdumped = 0;
}
//If this is the first time called, warn the user if we're too fast
if (linesdumped == 0) {
if (period < (numChannelsSampled * 200 + 600)) {
info("You are sampling close to the limit of NerdJack\n");
info("Sample fewer channels or sample slower\n");
}
}
//Now destination structure array is set as well as numDuplicates.
int totalGroups = NERDJACK_NUM_SAMPLES / numChannelsSampled;
//Loop forever to grab data
while((charsread = recv_all_timeout(data_fd,buf,NERDJACK_PACKET_SIZE,0,
& (struct timeval) { .tv_sec = NERDJACK_TIMEOUT }))){
while ((charsread =
recv_all_timeout(data_fd, &buf, NERDJACK_PACKET_SIZE, 0,
&(struct timeval) {
.tv_sec = expectedtimeout}))) {
//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;
}
if (charsread != NERDJACK_PACKET_SIZE) {
//There was a problem getting data. Probably a closed
//connection.
info("Packet timed out or was too short\n");
return -2;
}
//First check the header info
if(buf[0] != 0xF0 || buf[1] != 0xAA) {
printf("No Header info\n");
if (buf.headerone != 0xF0 || buf.headertwo != 0xAA) {
info("No Header info\n");
return -1;
}
//Check counter info to make sure not out of order
tempshort = (buf[2] << 8) | buf[3];
if(tempshort != currentcount ){
printf("Count wrong. Expected %hd but got %hd\n", currentcount, tempshort);
tempshort = ntohs(buf.packetNumber);
if (tempshort != *currentcount) {
info("Count wrong. Expected %hd but got %hd\n",
*currentcount, tempshort);
return -1;
}
//Increment number of packets received
currentcount++;
//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);
}
for(i = 0; i < destination[alignment].numCopies; i++) {
voltline[destination[alignment].destlist[i]] = volts;
}
} else {
for(i = 0; i < destination[alignment].numCopies; i++) {
dataline[destination[alignment].destlist[i]] = datapoint;
}
//Increment number of packets received
*currentcount = *currentcount + 1;
adcused = ntohs(buf.adcused);
packetsready = ntohs(buf.packetsready);
numgroupsProcessed = 0;
if (showmem) {
printf("%hd %hd\n", adcused, packetsready);
continue;
}
//While there is still more data in the packet, process it
while (numgroupsProcessed < totalGroups) {
//Poison the data structure
switch (convert) {
case CONVERT_VOLTS:
memset(voltline, 0,
numChannels * sizeof(double));
break;
default:
case CONVERT_HEX:
case CONVERT_DEC:
memset(dataline, 0,
numChannels * sizeof(unsigned char));
}
//Each point is two bytes, so increment index and total bytes read
index++;
index++;
alignment++;
//totalread++;
//Since channel data is packed, we need to know when to insert a newline
if(alignment == numChannelsSampled){
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) {
//Read in each group
for (i = 0; i < numChannels; i++) {
//Get the datapoint associated with the desired channel
datapoint =
ntohs(buf.data[channel_list[i] +
numgroupsProcessed *
numChannelsSampled]);
//Place it into the line
switch (convert) {
case CONVERT_VOLTS:
if (channel_list[i] <= 5) {
volts =
(double)(datapoint /
32767.0) *
((precision & 0x01) ? 5.0 :
10.0);
} else {
volts =
(double)(datapoint /
32767.0) *
((precision & 0x02) ? 5.0 :
10.0);
}
voltline[i] = volts;
break;
default:
case CONVERT_HEX:
case CONVERT_DEC:
dataline[i] =
(unsigned short)(datapoint -
INT16_MIN);
break;
}
}
//We want to dump the first line because it's usually spurious
if (linesdumped != 0) {
//Now print the group
switch (convert) {
case CONVERT_VOLTS:
for (i = 0; i < numChannels; i++) {
if (printf("%lf ", voltline[i])
< 0)
goto bad;
}
break;
case CONVERT_HEX:
for (i = 0; i < numChannels; i++) {
if (printf("%04hX", dataline[i])
< 0)
goto bad;
}
break;
default:
case CONVERT_DEC:
for (i = 0; i < numChannels; i++) {
if (printf("%hu ", dataline[i])
< 0)
goto bad;
}
break;
}
if (printf("\n") < 0)
goto bad;
//If we're counting lines, decrement them
if (lines != 0) {
linesleft--;
if (linesleft == 0) {
return 0;
}
}
} else {
linesdumped = linesdumped + 1;
}
//We've processed this group, so advance the counter
numgroupsProcessed++;
}
index = 0;
}
return 0;
bad:
info("Output error (disk full?)\n");
return -3;
}
int nerd_open(const char *address,int port) {
struct hostent *he;
net_init();
int32_t i32SocketFD = socket(PF_INET, SOCK_STREAM, 0);
/* Open a connection to the NerdJack */
int nerd_open(const char *address, int port)
{
if(-1 == i32SocketFD)
{
printf("cannot create socket");
return -1;
}
/* Set nonblocking */
struct hostent *he;
net_init();
int32_t i32SocketFD = socket(PF_INET, SOCK_STREAM, 0);
if (-1 == i32SocketFD) {
verb("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));
struct sockaddr_in stSockAddr;
memset(&stSockAddr, 0, sizeof(stSockAddr));
stSockAddr.sin_family = AF_INET;
stSockAddr.sin_port = htons(port);
he = gethostbyname(address);
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);
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) {
/* Connect */
if (connect_timeout
(i32SocketFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr),
&(struct timeval) {
.tv_sec = 3}) < 0) {
verb("connection to %s:%d failed: %s\n",
inet_ntoa(stSockAddr.sin_addr), port, compat_strerror(errno));
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]);
}
//Generate an appropriate sample initiation command
int
nerd_generate_command(getPacket * command, int *channel_list,
int channel_count, int precision, unsigned long period)
{
short channelbit = 0;
int i;
int highestchannel = 0;
for (i = 0; i < channel_count; i++) {
if (channel_list[i] > highestchannel) {
highestchannel = channel_list[i];
}
//channelbit = channelbit | (0x1 << channel_list[i]);
}
for (i = 0; i <= highestchannel; i++) {
channelbit = channelbit | (0x01 << i);
}
command->word[0] = 'G';
command->word[1] = 'E';
command->word[2] = 'T';
command->word[3] = 'D';
command->channelbit = htons(channelbit);
command->precision = precision;
command->period = htonl(period);
command->prescaler = 0;
return 0;
sprintf(command,"GET%3.3X%d%5.5d", channelbit,precision,period);
return 0;
}
int nerd_close_conn(int data_fd)

View File

@ -16,29 +16,51 @@
#include "netutil.h"
#define NERDJACK_CHANNELS 12
#define NERDJACK_CLOCK_RATE 54000000
#define NERDJACK_CLOCK_RATE 66000000
#define NERDJACK_DATA_PORT 49155
#define NERDJACK_UDP_RECEIVE_PORT 49156
#define NERDJACK_COMMAND_PORT 49157
#define NERDJACK_PACKET_SIZE 1460
#define NERDJACK_NUM_SAMPLES 724
#define NERDJACK_NUM_SAMPLES 726
/* Packet structure used in message to start sampling on NerdJack */
typedef struct __attribute__ ((__packed__)) {
char word[4];
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;
} getPacket;
/* Open/close TCP/IP connection to the NerdJack */
int nerd_open(const char *address,int port);
int nerd_open(const char *address, int port);
int nerd_close_conn(int data_fd);
/* Generate the command word for the NerdJack */
int nerd_generate_command(char * command, int * channel_list, int channel_count, int precision,
unsigned short period);
int nerd_generate_command(getPacket * command, int *channel_list,
int channel_count, int precision,
unsigned long period);
/* Send given command to NerdJack */
int nerd_send_command(const char *address, void *command, int length);
/* Get the version string from NerdJack */
int nerd_get_version(const char *address);
/* Stream data out of the NerdJack */
int nerd_data_stream(int data_fd, char * command, int numChannels, int * channel_list, int precision, int convert, int lines);
int nerd_data_stream(int data_fd, int numChannels, int *channel_list,
int precision, int convert, int lines, int showmem,
unsigned short *currentcount, unsigned int period,
int wasreset);
/* Detect the IP Address of the NerdJack and return in ipAddress */
int nerdjack_detect(char * ipAddress);
int nerdjack_detect(char *ipAddress);
/* Choose the best ScanConfig and ScanInterval parameters for the
desired scanrate. Returns -1 if no valid config found */
int nerdjack_choose_scan(double desired_rate, double *actual_rate, int *period);
int nerdjack_choose_scan(double desired_rate, double *actual_rate,
unsigned long *period);
#endif

View File

@ -39,15 +39,15 @@ int soblock(int socket, int blocking)
/* 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
connect_timeout(int s, const struct sockaddr *serv_addr, socklen_t addrlen,
struct timeval *timeout)
{
int ret;
fd_set writefds;
@ -60,7 +60,7 @@ int connect_timeout(int s, const struct sockaddr *serv_addr, socklen_t addrlen,
if (ret == 0) {
/* Success */
return 0;
return 0;
}
/* Check for immediate failure */
@ -70,7 +70,7 @@ int connect_timeout(int s, const struct sockaddr *serv_addr, socklen_t addrlen,
return -1;
#else
if (ret < 0 && errno != EINPROGRESS && errno != EALREADY)
return -1;
return -1;
#endif
/* In progress, wait for result. */
@ -114,12 +114,13 @@ int connect_timeout(int s, const struct sockaddr *serv_addr, socklen_t addrlen,
/* 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)
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);
@ -139,12 +140,12 @@ ssize_t send_timeout(int s, const void *buf, size_t len, int 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)
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);
@ -164,12 +165,14 @@ ssize_t recv_timeout(int s, void *buf, size_t len, int 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)
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);
@ -189,8 +192,9 @@ ssize_t recvfrom_timeout(int s, void *buf, size_t len, int flags, struct sockadd
/* 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)
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;
@ -217,8 +221,9 @@ ssize_t send_all_timeout(int s, const void *buf, size_t len, int flags,
/* 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)
ssize_t
recv_all_timeout(int s, void *buf, size_t len, int flags,
struct timeval * timeout)
{
struct timeval tv;
size_t left = len;

View File

@ -11,11 +11,19 @@
# define socklen_t int
# define in_addr_t uint32_t
# define in_port_t uint16_t
# include <windows.h>
# include <iphlpapi.h>
# define USE_IPHLPAPI 1
#else
# include <sys/socket.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <netdb.h>
# include <net/if.h>
# include <sys/ioctl.h>
#ifndef _SIZEOF_ADDR_IFREQ
#define _SIZEOF_ADDR_IFREQ(x) sizeof(x)
#endif
#endif
/* Initialize networking */
@ -26,20 +34,21 @@ 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,
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,
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,
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,
ssize_t recv_all_timeout(int s, void *buf, size_t len, int flags,
struct timeval *timeout);
#endif

92
opt.c
View File

@ -11,70 +11,71 @@
#include <string.h>
#include "opt.h"
void opt_init(int *optind) {
*optind=0;
void opt_init(int *optind)
{
*optind = 0;
}
char opt_parse(int argc, char **argv, int *optind, char **optarg,
struct options *opt) {
char
opt_parse(int argc, char **argv, int *optind, char **optarg,
struct options *opt)
{
char c;
int i;
(*optind)++;
if(*optind>=argc)
if (*optind >= argc)
return 0;
if(argv[*optind][0]=='-' &&
argv[*optind][1]!='-' &&
argv[*optind][1]!=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)
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)
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);
if (opt[i].shortopt == 0) {
fprintf(stderr, "Error: unknown option '-%c'\n", c);
return '?';
}
if(opt[i].arg==NULL)
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);
if (*optind >= argc || (argv[*optind][0] == '-' &&
argv[*optind][1] != 0)) {
fprintf(stderr, "Error: option '-%c' requires an "
"argument\n", c);
return '?';
}
(*optarg)=argv[*optind];
}
(*optarg) = argv[*optind];
return c;
} else if(argv[*optind][0]=='-' &&
argv[*optind][1]=='-' &&
argv[*optind][2]!=0) {
} 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)
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",
if (opt[i].shortopt == 0) {
fprintf(stderr, "Error: unknown option '%s'\n",
argv[*optind]);
return '?';
}
if(opt[i].arg==NULL)
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]);
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];
}
(*optarg) = argv[*optind];
return c;
} else {
/* End of options */
@ -82,14 +83,15 @@ char opt_parse(int argc, char **argv, int *optind, char **optarg,
}
}
void opt_help(struct options *opt, FILE *out) {
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);
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);
}
}

4
opt.h
View File

@ -11,7 +11,7 @@
#include <stdlib.h>
struct options {
struct options {
char shortopt;
char *longopt;
char *arg;
@ -23,6 +23,6 @@ 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);
void opt_help(struct options *opt, FILE * out);
#endif

421
ue9.c
View File

@ -24,11 +24,10 @@
#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)
void ue9_checksum_normal(uint8_t * buffer, size_t len)
{
uint16_t sum = 0;
@ -37,7 +36,7 @@ void ue9_checksum_normal(uint8_t *buffer, size_t len)
exit(1);
}
while (--len >= 1)
while (--len >= 1)
sum += (uint16_t) buffer[len];
sum = (sum / 256) + (sum % 256);
sum = (sum / 256) + (sum % 256);
@ -45,7 +44,7 @@ void ue9_checksum_normal(uint8_t *buffer, size_t len)
}
/* Fill checksums in data buffers, with "extended" checksum format */
void ue9_checksum_extended(uint8_t *buffer, size_t len)
void ue9_checksum_extended(uint8_t * buffer, size_t len)
{
uint16_t sum = 0;
@ -65,7 +64,7 @@ void ue9_checksum_extended(uint8_t *buffer, size_t len)
}
/* Verify checksums in data buffers, with "normal" checksum format. */
int ue9_verify_normal(uint8_t *buffer, size_t len)
int ue9_verify_normal(uint8_t * buffer, size_t len)
{
uint8_t saved, new;
@ -80,8 +79,7 @@ int ue9_verify_normal(uint8_t *buffer, size_t len)
buffer[0] = saved;
if (new != saved) {
verb("got %02x, expected %02x\n",
saved, new);
verb("got %02x, expected %02x\n", saved, new);
return 0;
}
@ -89,7 +87,7 @@ int ue9_verify_normal(uint8_t *buffer, size_t len)
}
/* Verify checksums in data buffers, with "extended" checksum format. */
int ue9_verify_extended(uint8_t *buffer, size_t len)
int ue9_verify_extended(uint8_t * buffer, size_t len)
{
uint8_t saved[3], new[3];
@ -109,20 +107,33 @@ int ue9_verify_extended(uint8_t *buffer, size_t len)
buffer[4] = saved[1];
buffer[5] = saved[2];
if (saved[0] != new[0] ||
saved[1] != new[1] ||
saved[2] != new[2]) {
if (saved[0] != new[0] || saved[1] != new[1] || saved[2] != new[2]) {
verb("got %02x %02x %02x, expected %02x %02x %02x\n",
saved[0], saved[1], saved[2], new[0], new[1], new[2]);
return 0;
}
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)
double
ue9_binary_to_analog(struct ue9Calibration *calib,
int gain, uint8_t resolution, uint16_t data)
{
double slope = 0, offset;
@ -136,13 +147,27 @@ double 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;
@ -157,7 +182,7 @@ double ue9_binary_to_analog(struct ue9Calibration *calib,
fprintf(stderr, "ue9_binary_to_analog: bad gain\n");
exit(1);
}
return data * slope + offset;
}
@ -165,7 +190,7 @@ double ue9_binary_to_analog(struct ue9Calibration *calib,
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 ue9_command(int fd, uint8_t * out, uint8_t * in, int inlen)
{
int extended = 0, outlen;
uint8_t saved_1, saved_3;
@ -184,8 +209,8 @@ 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 });
ret = send_all_timeout(fd, out, outlen, 0, &(struct timeval) {
.tv_sec = TIMEOUT});
if (ret < 0 || ret != outlen) {
verb("short send %d\n", (int)ret);
return -1;
@ -194,12 +219,12 @@ int ue9_command(int fd, uint8_t *out, uint8_t *in, int inlen)
/* 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)
if (extended)
saved_3 = out[3];
/* Receive result */
ret = recv_all_timeout(fd, in, inlen, 0,
& (struct timeval) { .tv_sec = UE9_TIMEOUT });
ret = recv_all_timeout(fd, in, inlen, 0, &(struct timeval) {
.tv_sec = TIMEOUT});
if (ret < 0 || ret != inlen) {
verb("short recv %d\n", (int)ret);
return -1;
@ -214,24 +239,23 @@ int ue9_command(int fd, uint8_t *out, uint8_t *in, int inlen)
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))
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 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)
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");
fprintf(stderr, "ue9_memory_read: buffer length must be 128\n");
exit(1);
}
@ -250,11 +274,11 @@ int ue9_memory_read(int fd, int blocknum, uint8_t *buffer, int len)
/* Got it */
memcpy(buffer, recvbuf + 8, len);
return 0;
return 0;
}
/* Convert 64-bit fixed point to double type */
double ue9_fp64_to_double(uint8_t *data)
double ue9_fp64_to_double(uint8_t * data)
{
int32_t a;
uint32_t b;
@ -271,43 +295,48 @@ 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);
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);
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);
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);
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);
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;
@ -318,7 +347,7 @@ 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));
@ -338,7 +367,7 @@ 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));
@ -373,11 +402,12 @@ 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;
}
/* Set initial window size hint to workaround LabJack firmware bug */
setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void *)&window_size,
setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void *)&window_size,
sizeof(window_size));
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void *)&window_size,
sizeof(window_size));
@ -388,19 +418,24 @@ 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);
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) {
if (connect_timeout(fd, (struct sockaddr *)&address, sizeof(address),
&(struct timeval) {
.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;
}
@ -409,7 +444,7 @@ int ue9_open(const char *host, int port)
void ue9_close(int fd)
{
/* does anyone actually call shutdown these days? */
shutdown(fd, 2 /* SHUT_RDWR */);
shutdown(fd, 2 /* SHUT_RDWR */ );
close(fd);
}
@ -423,12 +458,20 @@ double ue9_compute_rate(uint8_t scanconfig, uint16_t scaninterval)
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;
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;
@ -440,20 +483,25 @@ double ue9_compute_rate(uint8_t scanconfig, uint16_t 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
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 } };
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}};
/* Start with the fastest clock frequency. If the
scaninterval would be too large, knock it down until it
@ -461,7 +509,7 @@ int ue9_choose_scan(double desired_rate, double *actual_rate,
for (i = 0; valid[i].clock != 0; i++) {
double interval = valid[i].clock / desired_rate;
debug("Considering clock %lf (interval %lf)\n",
debug("Considering clock %lf (interval %lf)\n",
valid[i].clock, interval);
if (interval >= 0.5 && interval < 65535.5) {
@ -469,14 +517,14 @@ int ue9_choose_scan(double desired_rate, double *actual_rate,
*scaninterval = floor(interval + 0.5);
*scanconfig = valid[i].config;
*actual_rate = ue9_compute_rate(
*scanconfig, *scaninterval);
*actual_rate =
ue9_compute_rate(*scanconfig, *scaninterval);
debug("Config 0x%02x, desired %lf, actual %lf\n",
debug("Config 0x%02x, desired %lf, actual %lf\n",
*scanconfig, desired_rate, *actual_rate);
return 0;
}
}
}
return -1;
@ -486,8 +534,8 @@ int ue9_choose_scan(double desired_rate, double *actual_rate,
void ue9_buffer_flush(int fd)
{
uint8_t sendbuf[2], recvbuf[2];
sendbuf[1] = 0x08; /* FlushBuffer */
sendbuf[1] = 0x08; /* FlushBuffer */
if (ue9_command(fd, sendbuf, recvbuf, sizeof(recvbuf)) < 0) {
verb("command failed\n");
@ -498,14 +546,14 @@ void ue9_buffer_flush(int fd)
int ue9_stream_stop(int fd)
{
uint8_t sendbuf[2], recvbuf[4];
sendbuf[1] = 0xB0;
if (ue9_command(fd, sendbuf, recvbuf, sizeof(recvbuf)) < 0) {
verb("command failed\n");
return -1;
}
if (recvbuf[2] == STREAM_NOT_RUNNING || recvbuf[2] == 0)
return 0;
@ -517,14 +565,14 @@ int ue9_stream_stop(int fd)
int ue9_stream_start(int fd)
{
uint8_t sendbuf[2], recvbuf[4];
sendbuf[1] = 0xA8;
if (ue9_command(fd, sendbuf, recvbuf, sizeof(recvbuf)) < 0) {
verb("command failed\n");
return -1;
}
if (recvbuf[2] == 0)
return 0;
@ -534,27 +582,27 @@ int ue9_stream_start(int fd)
/* "Simple" stream configuration, assumes the channels are all
configured with the same gain. */
int ue9_streamconfig_simple(int fd, int *channel_list, int channel_count,
uint8_t scanconfig, uint16_t scaninterval,
uint8_t gain)
int
ue9_streamconfig_simple(int fd, int *channel_list, int channel_count,
uint8_t scanconfig, uint16_t scaninterval, uint8_t gain)
{
int i;
uint8_t buf[256];
/* 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[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 */
buf[13 + 2*i] = gain; /* Gain/bipolar setup */
buf[12 + 2 * i] = channel_list[i]; /* Channel number */
buf[13 + 2 * i] = gain; /* Gain/bipolar setup */
}
/* Send StreamConfig */
@ -562,7 +610,7 @@ int ue9_streamconfig_simple(int fd, int *channel_list, int channel_count,
debug("command failed\n");
return -1;
}
if (buf[6] != 0) {
verb("returned error %s\n", ue9_error(buf[6]));
return -1;
@ -571,10 +619,130 @@ int ue9_streamconfig_simple(int fd, int *channel_list, int channel_count,
return 0;
}
/* 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];
/* 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;
}
/* Set up TimerConfig command */
buf[1] = 0xF8; /* Extended command */
buf[2] = 0x0C; /* Command data words */
buf[3] = 0x18; /* TimerConfig */
buf[6] = divisor; /* TimerClockDivisor */
buf[7] = 0x80 | 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 < count) {
buf[10 + 3 * i] = mode_list[i];
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[28] = 0;
buf[29] = 0;
/* Send StreamConfig */
if (ue9_command(fd, buf, buf, 40) < 0) {
debug("command failed\n");
return -1;
}
if (buf[6] != 0) {
verb("returned error %s\n", ue9_error(buf[6]));
return -1;
}
debug("timer EnableStatus=0x%02x\n", buf[7]);
return 0;
}
/* Stream data and pass it to the data callback. If callback returns
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)
int
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];
@ -582,11 +750,11 @@ int ue9_stream_data(int fd, int channels,
int channel = 0;
int i;
uint16_t data[channels];
for (;;) {
/* Receive data */
ret = recv_all_timeout(fd, buf, 46, 0, & (struct timeval)
{ .tv_sec = UE9_TIMEOUT });
ret = recv_all_timeout(fd, buf, 46, 0, &(struct timeval) {
.tv_sec = TIMEOUT});
/* Verify packet format */
if (ret != 46) {
@ -594,8 +762,7 @@ int ue9_stream_data(int fd, int channels,
return -1;
}
if (!ue9_verify_extended(buf, 46) ||
!ue9_verify_normal(buf, 6)) {
if (!ue9_verify_extended(buf, 46) || !ue9_verify_normal(buf, 6)) {
verb("bad checksum\n");
return -2;
}
@ -617,13 +784,13 @@ int ue9_stream_data(int fd, int channels,
return -5;
}
packet++;
/* Check comm processor backlog (up to 512 kB) */
if (buf[45] & 0x80) {
verb("buffer overflow in CommBacklog, aborting\n");
return -6;
}
if ((buf[45] & 0x7f) > 112)
if ((buf[45] & 0x7f) > 112)
debug("warning: CommBacklog is high (%d bytes)\n",
(buf[45] & 0x7f) * 4096);
@ -638,16 +805,22 @@ int ue9_stream_data(int fd, int channels,
/* Read samples from the buffer */
for (i = 12; i <= 42; i += 2) {
data[channel++] = buf[i] + (buf[i+1] << 8);
if (channel < channels)
data[channel++] = buf[i] + (buf[i + 1] << 8);
if (channel < channels)
continue;
/* Received a full scan, send to callback */
channel = 0;
if ((*callback)(channels, data, context) < 0) {
if ((*callback) (channels, channel_list, gain_count, gain_list, data, context) < 0) {
/* We're done */
return 0;
}
}
}
}
/*
Local variables:
c-basic-offset: 8
End:
*/

43
ue9.h
View File

@ -42,7 +42,7 @@ struct ue9CommConfig {
in_addr_t address;
in_addr_t gateway;
in_addr_t subnet;
in_port_t portA;
in_port_t portA;
in_port_t portB;
uint8_t dhcp_enabled;
uint8_t product_id;
@ -68,31 +68,35 @@ 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
#define UE9_UNIPOLAR_GAIN8 0x03
#define UE9_BIPOLAR_GAIN1 0x08
#define UE9_CHANNELS 14
#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 */
void ue9_checksum_normal(uint8_t *buffer, size_t len);
void ue9_checksum_extended(uint8_t *buffer, size_t len);
void ue9_checksum_normal(uint8_t * buffer, size_t len);
void ue9_checksum_extended(uint8_t * buffer, size_t len);
/* Verify checksums in data buffers. Returns 0 on error. */
int ue9_verify_normal(uint8_t *buffer, size_t len);
int ue9_verify_extended(uint8_t *buffer, size_t len);
int ue9_verify_normal(uint8_t * buffer, size_t len);
int ue9_verify_extended(uint8_t * buffer, size_t len);
/* Open/close TCP/IP connection to the UE9 */
int ue9_open(const char *host, int port);
void ue9_close(int fd);
/* Read a memory block from the device. Returns -1 on error. */
int ue9_memory_read(int fd, int blocknum, uint8_t *buffer, int len);
int ue9_memory_read(int fd, int blocknum, uint8_t * buffer, int len);
/* Convert 64-bit fixed point to double type */
double ue9_fp64_to_double(uint8_t *data);
double ue9_fp64_to_double(uint8_t * data);
/* Retrieve calibration data or configuration from the device */
int ue9_get_calibration(int fd, struct ue9Calibration *calib);
@ -100,8 +104,11 @@ int ue9_get_comm_config(int fd, struct ue9CommConfig *config);
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);
double ue9_binary_to_analog(struct ue9Calibration *calib,
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);
@ -109,7 +116,7 @@ double ue9_compute_rate(uint8_t scanconfig, uint16_t scaninterval);
/* Choose the best ScanConfig and ScanInterval parameters for the
desired scanrate. Returns 0 if nothing can be chosen. */
int ue9_choose_scan(double desired_rate, double *actual_rate,
uint8_t *scanconfig, uint16_t *scaninterval);
uint8_t * scanconfig, uint16_t * scaninterval);
/* Flush data buffers */
void ue9_buffer_flush(int fd);
@ -124,18 +131,26 @@ int ue9_stream_start(int fd);
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 ue9_command(int fd, uint8_t * out, uint8_t * in, int inlen);
/* "Simple" stream configuration, assumes the channels are all
configured with the same gain. */
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 *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

View File

@ -1,2 +1,2 @@
/* This file was automatically generated. */
#define VERSION "1.0 (2009-01-21)"
#define VERSION "1.3.2 (2016-07-13)"