many random changes
[comedilib.git] / demo / ao_waveform.c
1 /*
2    This demo uses an analog output subdevice in timed
3    mode (mode 2) to generate a waveform.  The waveform
4    in this example is a sine wave (surprise!), but this
5    can be easily changed to make a general function
6    generator.
7
8    The function generation algorithm is the same as
9    what is typically used in digital function generators.
10    A 32-bit accumulator is incremented by a phase factor,
11    which is the amount (in radians) that the generator
12    advances each time step.  The accumulator is then
13    shifted right by 20 bits, to get a 12 bit offset into
14    a lookup table.  The value in the lookup table at
15    that offset is then put into a buffer for output to
16    the DAC.
17
18    [ Actually, the accumulator is only 26 bits, for some
19    reason.  I'll fix this sometime. ]
20
21    On the comedi side of things, the setup for mode 2
22    is similar to analog input, except for the TRIG_WRITE
23    flag.  Once you have issued the command, comedi then
24    expects you to keep the buffer full of data to output
25    to the DAC.  This is done by write().  Since there
26    may be a delay between the ioctl() and a subsequent
27    write(), you should fill the buffer using write() before
28    you call ioctl(), as is done here.
29
30    Also NOTE!  The lseek() to offset 1 is used to tell
31    comedi that you want to write to subdevice 1.  This
32    is not needed for analog input, since AI is usually on
33    subdevice 0.
34  */
35
36 #include <stdio.h>
37 #include <comedilib.h>
38 #include <fcntl.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 #include <sys/ioctl.h>
42 #include <errno.h>
43 #include <getopt.h>
44 #include <ctype.h>
45 #include <math.h>
46
47 #define dds_init dds_init_pseudocycloid
48
49
50 /* frequency of the sine wave to output */
51 double waveform_frequency       = 100.0;
52
53 /* update rate for the DAC, typically much higher than
54    the frequency of the sine wave. */
55 double update_frequency         = 50000.0;
56
57 /* peak-to-peak amplitude, in DAC units (i.e., 0-4095) */
58 double amplitude                = 4000;
59
60 /* offset, in DAC units */
61 double offset                   = 2048;
62
63 /* This is the size of chunks we deal with when creating and
64    outputting data.  This *could* be 1, but that would be
65    inefficient */
66 #define BUF_LEN         4096
67
68
69 #define N_SCANS         0
70 #define N_CHANS         1
71
72 int subdevice;
73 int channels[] = { 0 };
74 int range = 0;
75 int aref = AREF_GROUND;
76 int external_trigger_number = 0;
77
78 sampl_t data[BUF_LEN];
79
80 void dds_output(sampl_t *buf,int n);
81 void dds_init_sine(void);
82 void dds_init_pseudocycloid(void);
83
84 int main(int argc, char *argv[])
85 {
86         char *fn = NULL;
87         comedi_trig it;
88         int err;
89         int n,m,i;
90         int total=0;
91         comedi_t *dev;
92         double actual_freq;
93         unsigned int chan[N_CHANS];
94
95         if(argc>=2){
96                 waveform_frequency=atof(argv[1]);
97         }
98
99         fn = "/dev/comedi0";
100
101         dev = comedi_open(fn);
102
103         subdevice = comedi_find_subdevice_by_type(dev,COMEDI_SUBD_AO,0);
104
105         it.subdev = subdevice;
106         it.mode = 2;
107         it.flags = TRIG_WRITE;
108         it.n_chan = N_CHANS;
109         it.chanlist = chan;
110         it.data = NULL;
111         it.n = N_SCANS;
112         it.trigsrc = 0;
113
114         /* convert the frequency into a timer value */
115         comedi_get_timer(dev,subdevice,update_frequency,&it.trigvar,&actual_freq);
116         fprintf(stderr,"primary actual frequency=%g timer value=%d\n",actual_freq,it.trigvar);
117
118         /* pack the channel list */
119         for(i=0;i<N_CHANS;i++){
120                 chan[i] = CR_PACK(channels[i], range, aref);
121         }
122
123         dds_init();
124
125         dds_output(data,BUF_LEN);
126         dds_output(data,BUF_LEN);
127
128         lseek(comedi_fileno(dev),subdevice,SEEK_SET);
129         m=write(comedi_fileno(dev),data,BUF_LEN*sizeof(sampl_t));
130         perror("write");
131         printf("m=%d\n",m);
132
133
134         if ((err = comedi_trigger(dev, &it)) < 0) {
135                 perror("ioctl");
136                 exit(1);
137         }
138         while(1){
139                 dds_output(data,BUF_LEN);
140                 n=BUF_LEN*sizeof(sampl_t);
141                 while(n>0){
142                         m=write(comedi_fileno(dev),(void *)data+(BUF_LEN*sizeof(sampl_t)-n),n);
143                         if(m<0){
144                                 perror("write");
145                                 exit(0);
146                         }
147                         //printf("m=%d\n",m);
148                         n-=m;
149                 }
150                 total+=BUF_LEN;
151                 //printf("%d\n",total);
152         }
153
154         return 0;
155 }
156
157
158
159 #define WAVEFORM_SHIFT 16
160 #define WAVEFORM_LEN (1<<WAVEFORM_SHIFT)
161 #define WAVEFORM_MASK (WAVEFORM_LEN-1)
162
163
164 sampl_t waveform[WAVEFORM_LEN];
165
166 unsigned int acc;
167 unsigned int adder;
168
169 void dds_init(void)
170 {
171         int i;
172
173         adder=waveform_frequency/update_frequency*(1<<16)*(1<<WAVEFORM_SHIFT);
174
175         dds_init_sine();
176
177         /* this is due to a bug in the NI-E driver */
178         if(range){
179                 for(i=0;i<WAVEFORM_LEN;i++){
180                         waveform[i]^=0x800;
181                 }
182         }
183 }
184
185 void dds_init_sine(void)
186 {
187         int i;
188
189         for(i=0;i<WAVEFORM_LEN;i++){
190                 waveform[i]=rint(offset+0.5*amplitude*cos(i*2*M_PI/WAVEFORM_LEN));
191         }
192 }
193
194 /* Yes, I know this is not the proper equation for a
195    cycloid.  Fix it. */
196 void dds_init_cycloid(void)
197 {
198         int i;
199         double t;
200
201         for(i=0;i<WAVEFORM_LEN/2;i++){
202                 t=2*((double)i)/WAVEFORM_LEN;
203                 waveform[i]=rint(offset+amplitude*sqrt(1-4*t*t));
204         }
205         for(i=WAVEFORM_LEN/2;i<WAVEFORM_LEN;i++){
206                 t=2*(1-((double)i)/WAVEFORM_LEN);
207                 waveform[i]=rint(offset+amplitude*sqrt(1-t*t));
208         }
209 }
210
211 void dds_init_sawtooth(void)
212 {
213         int i;
214
215         for(i=0;i<WAVEFORM_LEN;i++){
216                 waveform[i]=rint(offset+amplitude*((double)i)/WAVEFORM_LEN);
217         }
218 }
219
220 void dds_output(sampl_t *buf,int n)
221 {
222         int i;
223         sampl_t *p=buf;
224
225         for(i=0;i<n;i++){
226                 *p=waveform[(acc>>16)&WAVEFORM_MASK];
227                 p++;
228                 acc+=adder;
229         }
230 }
231