fef1d2c208cf8cd1bc72bfd60e828e0523c9ec73
[comedilib.git] / demo / ao_waveform.c
1 /*
2  * Asynchronous Analog Output Example
3  * Part of Comedilib
4  *
5  * Copyright (c) 1999,2000 David A. Schleef <ds@schleef.org>
6  *
7  * This file may be freely modified, distributed, and combined with
8  * other software, as long as proper attribution is given in the
9  * source code.
10  */
11
12 /*
13  * Requirements: Analog output device capable of
14  *    asynchronous commands.
15  *
16  * This demo uses an analog output subdevice with an
17  * asynchronous command to generate a waveform.  The
18  * demo hijacks for -n option to select a waveform from
19  * a predefined list.  The default waveform is a sine
20  * wave (surprise!).  Other waveforms include sawtooth,
21  * square, triangle and cycloid.
22  *
23  * The function generation algorithm is the same as
24  * what is typically used in digital function generators.
25  * A 32-bit accumulator is incremented by a phase factor,
26  * which is the amount (in radians) that the generator
27  * advances each time step.  The accumulator is then
28  * shifted right by 20 bits, to get a 12 bit offset into
29  * a lookup table.  The value in the lookup table at
30  * that offset is then put into a buffer for output to
31  * the DAC.
32  *
33  * [ Actually, the accumulator is only 26 bits, for some
34  * reason.  I'll fix this sometime. ]
35  *
36  */
37
38 #include <stdio.h>
39 #include <comedilib.h>
40 #include <fcntl.h>
41 #include <stdlib.h>
42 #include <unistd.h>
43 #include <errno.h>
44 #include <getopt.h>
45 #include <ctype.h>
46 #include <math.h>
47 #include <string.h>
48 #include "examples.h"
49
50
51 /* frequency of the sine wave to output */
52 double waveform_frequency       = 10.0;
53
54 /* peak-to-peak amplitude, in DAC units (i.e., 0-4095) */
55 double amplitude                = 4000;
56
57 /* offset, in DAC units */
58 double offset                   = 2048;
59
60 /* This is the size of chunks we deal with when creating and
61    outputting data.  This *could* be 1, but that would be
62    inefficient */
63 #define BUF_LEN 0x8000
64
65 int external_trigger_number = 0;
66
67 sampl_t data[BUF_LEN];
68
69 void dds_output(sampl_t *buf,int n);
70 void dds_init(double waveform_frequency, double update_frequency, int fn);
71
72 void dds_init_sine(void);
73 void dds_init_pseudocycloid(void);
74 void dds_init_cycloid(void);
75 void dds_init_ramp_up(void);
76 void dds_init_ramp_down(void);
77 void dds_init_triangle(void);
78 void dds_init_square(void);
79 void dds_init_blancmange(void);
80
81 static void (* const dds_init_function[])(void) = {
82         dds_init_sine,
83         dds_init_ramp_up,
84         dds_init_ramp_down,
85         dds_init_triangle,
86         dds_init_square,
87         dds_init_cycloid,
88         dds_init_blancmange,
89 };
90
91 #define NUMFUNCS        (sizeof(dds_init_function)/sizeof(dds_init_function[0]))
92
93 int main(int argc, char *argv[])
94 {
95         comedi_cmd cmd;
96         int err;
97         int n,m;
98         int total=0;
99         comedi_t *dev;
100         unsigned int chanlist[16];
101         unsigned int maxdata;
102         comedi_range *rng;
103         int ret;
104         struct parsed_options options;
105         int fn;
106
107         init_parsed_options(&options);
108         options.subdevice = -1;
109         options.n_chan = 0;     /* default waveform */
110         parse_options(&options, argc, argv);
111
112         /* Use n_chan to select waveform (cheat!) */
113         fn = options.n_chan;
114         if(fn < 0 || fn >= NUMFUNCS){
115                 fn = 0;
116         }
117
118         /* Force n_chan to be 1 */
119         options.n_chan = 1;
120
121         if(options.value){
122                 waveform_frequency = options.value;
123         }
124
125         dev = comedi_open(options.filename);
126         if(dev == NULL){
127                 fprintf(stderr, "error opening %s\n", options.filename);
128                 return -1;
129         }
130         if(options.subdevice < 0)
131                 options.subdevice = comedi_find_subdevice_by_type(dev, COMEDI_SUBD_AO, 0);
132
133         maxdata = comedi_get_maxdata(dev, options.subdevice, options.channel);
134         rng = comedi_get_range(dev, options.subdevice, options.channel, options.range);
135
136         offset = (double)comedi_from_phys(0.0, rng, maxdata);
137         amplitude = (double)comedi_from_phys(1.0, rng, maxdata) - offset;
138
139         memset(&cmd,0,sizeof(cmd));
140         cmd.subdev = options.subdevice;
141         cmd.flags = CMDF_WRITE;
142         cmd.start_src = TRIG_INT;
143         cmd.start_arg = 0;
144         cmd.scan_begin_src = TRIG_TIMER;
145         cmd.scan_begin_arg = 1e9 / options.freq;
146         cmd.convert_src = TRIG_NOW;
147         cmd.convert_arg = 0;
148         cmd.scan_end_src = TRIG_COUNT;
149         cmd.scan_end_arg = options.n_chan;
150         cmd.stop_src = TRIG_NONE;
151         cmd.stop_arg = 0;
152
153         cmd.chanlist = chanlist;
154         cmd.chanlist_len = options.n_chan;
155
156         chanlist[0] = CR_PACK(options.channel, options.range, options.aref);
157         //chanlist[1] = CR_PACK(options.channel + 1, options.range, options.aref);
158
159         dds_init(waveform_frequency, options.freq, fn);
160
161         dump_cmd(stdout,&cmd);
162
163         err = comedi_command_test(dev, &cmd);
164         if (err < 0) {
165                 comedi_perror("comedi_command_test");
166                 exit(1);
167         }
168
169         err = comedi_command_test(dev, &cmd);
170         if (err < 0) {
171                 comedi_perror("comedi_command_test");
172                 exit(1);
173         }
174
175         if ((err = comedi_command(dev, &cmd)) < 0) {
176                 comedi_perror("comedi_command");
177                 exit(1);
178         }
179
180         dds_output(data,BUF_LEN);
181         n = BUF_LEN * sizeof(sampl_t);
182         m = write(comedi_fileno(dev), (void *)data, n);
183         if(m < 0){
184                 perror("write");
185                 exit(1);
186         }else if(m < n)
187         {
188                 fprintf(stderr, "failed to preload output buffer with %i bytes, is it too small?\n"
189                         "See the --write-buffer option of comedi_config\n", n);
190                 exit(1);
191         }
192         printf("m=%d\n",m);
193
194         ret = comedi_internal_trigger(dev, options.subdevice, 0);
195         if(ret < 0){
196                 perror("comedi_internal_trigger\n");
197                 exit(1);
198         }
199
200         while(1){
201                 dds_output(data,BUF_LEN);
202                 n=BUF_LEN*sizeof(sampl_t);
203                 while(n>0){
204                         m=write(comedi_fileno(dev),(void *)data+(BUF_LEN*sizeof(sampl_t)-n),n);
205                         if(m<0){
206                                 perror("write");
207                                 exit(0);
208                         }
209                         printf("m=%d\n",m);
210                         n-=m;
211                 }
212                 total+=BUF_LEN;
213                 //printf("%d\n",total);
214         }
215
216         return 0;
217 }
218
219
220
221 #define WAVEFORM_SHIFT 16
222 #define WAVEFORM_LEN (1<<WAVEFORM_SHIFT)
223 #define WAVEFORM_MASK (WAVEFORM_LEN-1)
224
225
226 sampl_t waveform[WAVEFORM_LEN];
227
228 unsigned int acc;
229 unsigned int adder;
230
231 void dds_init(double waveform_frequency, double update_frequency, int fn)
232 {
233         adder = waveform_frequency / update_frequency * (1 << 16) * (1 << WAVEFORM_SHIFT);
234
235         (*dds_init_function[fn])();
236 }
237
238 void dds_output(sampl_t *buf,int n)
239 {
240         int i;
241         sampl_t *p=buf;
242
243         for(i=0;i<n;i++){
244                 *p=waveform[(acc>>16)&WAVEFORM_MASK];
245                 p++;
246                 acc+=adder;
247         }
248 }
249
250 /* Defined for x in [0,1] */
251 static inline double triangle(double x)
252 {
253         return (x > 0.5) ? 1.0 - x : x;
254 }
255
256 void dds_init_sine(void)
257 {
258         int i;
259         double ofs = offset;
260         double amp = 0.5 * amplitude;
261
262         if(ofs < amp){
263                 /* Probably a unipolar range.  Bump up the offset. */
264                 ofs = amp;
265         }
266         for(i=0;i<WAVEFORM_LEN;i++){
267                 waveform[i]=rint(ofs+amp*cos(i*2*M_PI/WAVEFORM_LEN));
268         }
269 }
270
271 /* Yes, I know this is not the proper equation for a
272    cycloid.  Fix it. */
273 void dds_init_pseudocycloid(void)
274 {
275         int i;
276         double t;
277
278         for(i=0;i<WAVEFORM_LEN/2;i++){
279                 t=2*((double)i)/WAVEFORM_LEN;
280                 waveform[i]=rint(offset+amplitude*sqrt(1-4*t*t));
281         }
282         for(i=WAVEFORM_LEN/2;i<WAVEFORM_LEN;i++){
283                 t=2*(1-((double)i)/WAVEFORM_LEN);
284                 waveform[i]=rint(offset+amplitude*sqrt(1-t*t));
285         }
286 }
287
288 void dds_init_cycloid(void)
289 {
290         enum { SUBSCALE = 2 };  /* Needs to be >= 2. */
291         int h, i, ni;
292         double t, x, y;
293
294         i = -1;
295         for (h = 0; h < WAVEFORM_LEN * SUBSCALE; h++){
296                 t = (h * (2 * M_PI)) / (WAVEFORM_LEN * SUBSCALE);
297                 x = t - sin(t);
298                 ni = (int)floor((x * WAVEFORM_LEN) / (2 * M_PI));
299                 if (ni > i) {
300                         i = ni;
301                         y = 1 - cos(t);
302                         waveform[i] = rint(offset + (amplitude * y / 2));
303                 }
304         }
305 }
306
307 void dds_init_ramp_up(void)
308 {
309         int i;
310
311         for(i=0;i<WAVEFORM_LEN;i++){
312                 waveform[i]=rint(offset+amplitude*((double)i)/WAVEFORM_LEN);
313         }
314 }
315
316 void dds_init_ramp_down(void)
317 {
318         int i;
319
320         for(i=0;i<WAVEFORM_LEN;i++){
321                 waveform[i]=rint(offset+amplitude*((double)(WAVEFORM_LEN-1-i))/WAVEFORM_LEN);
322         }
323 }
324
325 void dds_init_triangle(void)
326 {
327         int i;
328
329         for (i = 0; i < WAVEFORM_LEN; i++) {
330                 waveform[i] = rint(offset + amplitude * 2 * triangle((double)i / WAVEFORM_LEN));
331         }
332 }
333
334 void dds_init_square(void)
335 {
336         int i;
337
338         for (i = 0; i < WAVEFORM_LEN / 2; i++) {
339                 waveform[i] = rint(offset);
340         }
341         for ( ; i < WAVEFORM_LEN; i++) {
342                 waveform[i] = rint(offset + amplitude);
343         }
344 }
345
346 void dds_init_blancmange(void)
347 {
348         int i, n;
349         double b, x;
350
351         for (i = 0; i < WAVEFORM_LEN; i++) {
352                 b = 0;
353                 for (n = 0; n < 16; n++) {
354                         x = (double)i / WAVEFORM_LEN;
355                         x *= (1 << n);
356                         x -= floor(x);
357                         b += triangle(x) / (1 << n);
358                 }
359                 waveform[i] = rint(offset + amplitude * 1.5 * b);
360         }
361 }