File: saw.c

package info (click to toggle)
eegdev 0.2-3
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 4,972 kB
  • sloc: ansic: 32,259; sh: 10,941; makefile: 247; lex: 128; yacc: 118
file content (271 lines) | stat: -rw-r--r-- 7,958 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
/*
    Copyright (C) 2011-2012  EPFL (Ecole Polytechnique Fédérale de Lausanne)
    Laboratory CNBI (Chair in Non-Invasive Brain-Machine Interface)
    Nicolas Bourdaud <nicolas.bourdaud@gmail.com>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <stdint.h>
#include <errno.h>
#include <time.h>

#include <eegdev-pluginapi.h>


struct saw_eegdev {
	struct devmodule dev;
	int fs;
	pthread_t thread_id;
	
	char tmplabel[16];
};

#define get_saw(dev_p) ((struct saw_eegdev*)(dev_p))

#define SAMPLINGRATE	256
#define NUM_EEG_CH	8
#define NUM_TRI_CH	1
#define NS		2	// Number of sample transfered in one update
#define SAWFREQ		50	// Frequency of the sawtooth function in
				// number of sample


#define NCH	(NUM_EEG_CH + NUM_TRI_CH)

#define NSEC_IN_SEC	1000000000

/******************************************************************
 *                       sawtooth metadata                   	  *
 ******************************************************************/
static const char saw_device_type[] = "Sawtooth function generator";
static const char saw_device_id[] = "N/A";
static const char* const sawunit[2] = {"uV", "Boolean"};
static const char* const sawtransducter[2] = {"Fake electrode", "Trigger"};
static const int saw_provided_stypes[] = {EGD_EEG, EGD_TRIGGER};
static const struct egdi_optname saw_options[] = {
	{.name = "samplingrate", .defvalue = "256"},
	{.name = NULL}
};

static
void sawtooth_func(int32_t* data, long isample)
{
	int i;

	for (i=0; i<NUM_EEG_CH; i++)
		data[i] = (i+1)*((isample % SAWFREQ) - SAWFREQ/2);

	for (i=0; i<NUM_TRI_CH; i++)
		data[i+NUM_EEG_CH] = (isample % SAWFREQ) ? 0 : (0xAA << i);
}


/* Acquisition loop function

Comment: This function use clock_gettime and clock_nanosleep. Although those
functions are part of POSIX.1-2001, they are not always present by default
on some platform (MacOSX and Windows). If this code is only meant for
testing purpose, just replace them with equivalents. If this code is meant
for real device implementation, keep using them but provides replacement
for platform that don't have them (see the source code of eegdev for an
example of how to do it).
*/
static
void* acq_loop_fn(void* arg)
{
	struct saw_eegdev* sawdev = arg;
	const struct core_interface* ci = &sawdev->dev.ci;
	struct devmodule* dev = &sawdev->dev;
	int32_t data[NCH*NS] = {0};
	struct timespec ts;
	long isample = 0;
	int i;

	// Initialize ts with the current time
	// (note: see earlier comment)
	clock_gettime(CLOCK_REALTIME, &ts);

	// Make sure that we can call pthread_cancel from the main thread
	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
	pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);

	while (1) {
		// Test whether the acquisition thread should stop.
		// This is often not necessary to call it with non trivial
		// device since those often call for their acquisition
		// function that are cancellation point.
		// (see pthreads manpage)
		pthread_testcancel();

		// Set the timestamp to the next transfer and wait for it.
		ts.tv_nsec += NS*(NSEC_IN_SEC / sawdev->fs);
		if (ts.tv_nsec >= NSEC_IN_SEC) {
			ts.tv_nsec -= NSEC_IN_SEC;
			ts.tv_sec++;
		}
		clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &ts, NULL);

		// Write sample in the array (implement sawtooth function)
		for (i=0; i<NS; i++)
			sawtooth_func(&data[i*NCH], isample++);

		// Update the eegdev structure with the new data
		// Failure in this function already report error in the core
		if (ci->update_ringbuffer(dev, data, sizeof(data)))
			break;
	}
	
	// If we reach here, an error has occured before
	return NULL;
}


/******************************************************************
 *               sawtooth methods implementation                  *
 ******************************************************************/
static
int saw_open_device(struct devmodule* dev, const char* optv[])
{
	int ret; 
	pthread_t* pthid;
	struct saw_eegdev* sawdev = get_saw(dev);
	struct systemcap cap;

	// The core library populates optv array with the setting values in
	// the order of their declaration in the array assigned in the
	// supported_opts field of the egdi_plugin_info structure. If the
	// user does not specify any value for a specific setting, it
	// receive its default value as defined in the defvalue field.
	// So in this particular case, optv[0] correspond to the value of
	// "samplerate" setting whose default value is "256" (see the
	// previous declaration of saw_options).
	sawdev->fs = atoi(optv[0]);

	// Specify the capabilities of a saw device
	cap.sampling_freq = sawdev->fs;
	cap.type_nch[EGD_EEG] = NUM_EEG_CH;
	cap.type_nch[EGD_TRIGGER] = NUM_TRI_CH;
	cap.type_nch[EGD_SENSOR] = 0;
	cap.device_type = saw_device_type;
	cap.device_id = saw_device_id;
	dev->ci.set_cap(dev, &cap);
	dev->ci.set_input_samlen(dev, NCH*sizeof(int32_t));

	// Create the acquisition thread
	pthid = &sawdev->thread_id;
	ret = pthread_create(pthid, NULL, acq_loop_fn, sawdev);
	if (ret) {
		errno = ret;
		return -1;
	}
	
	return 0;
}


static
int saw_close_device(struct devmodule* dev)
{
	struct saw_eegdev* sawdev = get_saw(dev);

	// Stop acquisition thread
	pthread_cancel(sawdev->thread_id);
	pthread_join(sawdev->thread_id, NULL);
	
	return 0;
}


static
int saw_set_channel_groups(struct devmodule* dev, unsigned int ngrp,
					const struct grpconf* grp)
{
	unsigned int i, t;
	struct selected_channels* selch;
	const int soff[2] = {0, NUM_EEG_CH};
	
	if (!(selch = dev->ci.alloc_input_groups(dev, ngrp)))
		return -1;

	for (i=0; i<ngrp; i++) {
		t = (grp[i].sensortype == EGD_TRIGGER) ? 1 : 0;

		// Set parameters of (eeg -> ringbuffer)
		selch[i].in_offset = (soff[t]+grp[i].index)*sizeof(int32_t);
		selch[i].inlen = grp[i].nch*sizeof(int32_t);
		selch[i].typein = EGD_INT32;
		selch[i].typeout = grp[i].datatype;
		selch[i].iarray = grp[i].iarray;
		selch[i].arr_offset = grp[i].arr_offset;
		if (t == 1)
			selch[i].bsc = 0;
		else {
			selch[i].bsc = 1;
			selch[i].sc.valfloat = (1.0f/8192.0f);
		}
	}
		
	return 0;
}


static
void saw_fill_chinfo(const struct devmodule* dev, int stype,
	                     unsigned int ich, struct egd_chinfo* info)
{
	int t;
	struct saw_eegdev* sawdev = get_saw(dev); 

	if (stype == EGD_EEG) {
		info->isint = 0;
		info->dtype = EGD_DOUBLE;
		info->min.valdouble = -262144.0;
		info->max.valdouble = 262143.96875;
		t = 0;
	} else {
		info->isint = 1;
		info->dtype = EGD_INT32;
		info->min.valint32_t = INT32_MIN;
		info->max.valint32_t = INT32_MAX;
		t = 1;
	}
	sprintf(sawdev->tmplabel, (t ? "tri:%i":"eeg:%i"), ich);
	info->label = sawdev->tmplabel;
	info->unit = sawunit[t];
	info->transducter = sawtransducter[t];
}


// All the device methods of the plugin are declared here.
// They _must_ be defined in a struct egdi_plugin_info named
// eegdev_plugin_info which is usually the only symbol exported by the
// dynamically shared object.
API_EXPORTED
const struct egdi_plugin_info eegdev_plugin_info = {
	.plugin_abi = 	EEGDEV_PLUGIN_ABI_VERSION,
	.struct_size = 	sizeof(struct saw_eegdev),
	.open_device = 		saw_open_device,
	.close_device = 	saw_close_device,
	.set_channel_groups = 	saw_set_channel_groups,
	.fill_chinfo = 		saw_fill_chinfo,
	.supported_opts =	saw_options
};