added configure options for disabling python and ruby bindings
[comedilib.git] / doc / tutorial.sgml
1 <!-- <!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V3.1//EN" "docbook/dtd/3.1/docbook.dtd">  -->
2
3 <section id="writingprograms">
4 <title>
5 Writing &comedi; programs
6 </title>
7 <para>
8 This Section describes how a well-installed and configured &comedi;
9 package can be used in an application, to communicate data with a set
10 of &comedi; devices.
11 <xref linkend="acquisitionfunctions"> gives more details about
12 the various acquisition functions with which the application
13 programmer can perform data acquisition in &comedi;.
14 </para>
15 <para>
16 Also don't forget to take a good look at the
17 <filename class=directory>demo</filename>
18 directory of the Comedilib source code. It contains lots of examples
19 for the basic functionalities of &comedi;.
20 </para>
21
22 <section id="firstprogram">
23 <title>
24 Your first &comedi; program
25 </title>
26
27 <para>
28 This example requires a card that has analog or digital input. This
29 progam opens the device, gets the data, and prints it out:
30 <programlisting>
31 #include <![CDATA[<stdio.h>]]>   /* for printf() */
32 #include <![CDATA[<]]><link linkend="comedi-comedilib-h">comedilib.h</link><![CDATA[>]]>
33
34 int subdev = 0;         /* change this to your input subdevice */
35 int chan = 0;           /* change this to your channel */
36 int range = 0;          /* more on this later */
37 int aref = <link linkend="aref-ground">AREF_GROUND</link>; /* more on this later */
38
39 int main(int argc,char *argv[])
40 {
41   <link linkend="ref-type-comedi-t">comedi_t</link> *it;
42   <link linkend="ref-type-lsampl-t">lsampl_t</link> data;
43
44   it=<link linkend="func-ref-comedi-open">comedi_open</link>("/dev/comedi0");
45
46   <link linkend="func-ref-comedi-data-read">comedi_data_read</link>(it,subdev,chan,range,aref, & data);
47
48   printf("%d\n",data);
49
50   return 0;
51 }
52 </programlisting>
53 The
54 <function>
55  <link linkend="func-ref-comedi-open">comedi_open()</link>
56 </function> can only be successful if the
57 <filename>comedi0</filename> device file is configured to point to a
58 valid &comedi; driver. <xref linkend="cardconfiguration"> explains
59 how this driver is linked to the <quote>device file</quote>.
60 </para>
61 <para>
62 The code above is basically the guts of
63 <filename>demo/inp.c</filename>, without error checking or fancy
64 options.  Compile the program using
65 </para>
66
67 <screen>
68 cc tut1.c -lcomedi -o tut1
69 </screen>
70 <para>
71 (Replace <literal>cc</literal> by your favourite C compiler command.)
72 </para>
73
74 <para>
75 The <parameter class=function>range</parameter> variable tells
76 &comedi; which gain to use when measuring an analog voltage.  Since we
77 don't know (yet) which numbers are valid, or what each means, we'll
78 use <literal>0</literal>, because it won't cause errors.  Likewise
79 with <parameter class=function>aref</parameter>, which determines the
80 analog reference used.
81 </para>
82 </section>
83
84
85 <section id="convertingsamples">
86 <title>
87 Converting samples to voltages
88 </title>
89
90 <para>
91 If you selected an analog input subdevice, you probably noticed
92 that the output of <command>tut1</command> is a number between
93 <literal>0</literal> and <literal>4095</literal>, or
94 <literal>0</literal> and <literal>65535</literal>, depending on the
95 number of bits in the A/D converter. &comedi; samples are
96 <emphasis>always</emphasis> unsigned,
97 with <literal>0</literal>  representing the lowest voltage of the ADC,
98 and <literal>4095</literal>
99 the highest.  &comedi; compensates for anything else the manual for
100 your device says.  However, you probably prefer to have this number
101 translated to a voltage.  Naturally, as a good programmer, your first
102 question is: <quote>How do I do this in a device-independent
103 manner?</quote>
104 </para>
105
106 <para>
107 Most devices give you a choice of gain and unipolar/bipolar
108 input, and &comedi; allows you to select which of these to use.  This
109 parameter is called the <quote>range parameter,</quote> since it
110 specifies the <quote>input range</quote> for analog input (or
111 <quote>output range</quote> for analog output.)  The range parameter
112 represents both the gain and the unipolar/bipolar aspects.
113 </para>
114
115 <para>
116 &comedi; keeps the number of available ranges and the largest
117 sample value for each subdevice/channel combination.  (Some
118 devices allow different input/output ranges for different
119 channels in a subdevice.)
120 </para>
121
122 <para>
123 The largest sample value can be found using the function
124 <programlisting>
125  <link linkend="ref-type-lsampl-t">lsampl_t</link> <link linkend="func-ref-comedi-get-maxdata">comedi_get_maxdata</link>(<link linkend="ref-type-comedi-t">comedi_t</link> * device, unsigned int subdevice, unsigned int channel))
126 </programlisting>
127 The number of available ranges can be found using the function:
128 <programlisting>
129  int <link linkend="func-ref-comedi-get-n-ranges">comedi_get_n_ranges</link>(<link linkend="ref-type-comedi-t">comedi_t</link> * device, unsigned int subdevice, unsigned int channel);
130 </programlisting>
131 </para>
132
133 <para>
134 For each value of the range parameter for a particular
135 subdevice/channel, you can get range information using:
136 <programlisting>
137  <link linkend="ref-type-comedi-range">comedi_range</link> * <link linkend="func-ref-comedi-get-range">comedi_get_range</link>(<link linkend="ref-type-comedi-t">comedi_t</link> * device, 
138                                  unsigned int subdevice, unsigned int channel, unsigned int range);
139 </programlisting>
140 which returns a pointer to a
141 <link linkend="ref-type-comedi-range">comedi_range</link>
142 structure, which has the following contents:
143 <programlisting>
144 typedef struct{
145         double min;
146         double max;
147         unsigned int unit;
148 }comedi_range;
149 </programlisting>
150 The structure element <parameter class=function>min</parameter>
151 represents the voltage corresponding to
152 <link linkend="func-ref-comedi-data-read">comedi_data_read()</link>
153 returning <literal>0</literal>,
154 and <parameter class=function>max</parameter> represents
155 <link linkend="func-ref-comedi-data-read">comedi_data_read()</link>
156 returning <parameter class=function>maxdata</parameter>,
157 (i.e., <literal>4095</literal> for <literal>12</literal> bit A/C
158 converters, <literal>65535</literal> for <literal>16</literal> bit,
159 or, <literal>1</literal> for digital input; more on this in a bit.)
160 The <parameter class=function>unit</parameter> entry tells you if
161 <parameter class=function>min</parameter> and
162 <parameter class=function>max</parameter> refer to voltage, current,
163 or are dimensionless (e.g., for digital I/O).
164 </para>
165
166 <para>
167 <quote>Could it get easier?</quote> you say.  Well, yes.  Use
168 the function <function>comedi_to_phys()</function>
169 <link linkend="func-ref-comedi-to-phys">comedi_to_phys()</link>, which
170 converts data values to physical units.  Call it using something like
171 </para>
172
173 <programlisting>
174 volts=<link linkend="func-ref-comedi-to-phys">comedi_to_phys</link>(it,data,range,maxdata);
175 </programlisting>
176
177 <para>
178 and the opposite
179 </para>
180
181 <programlisting>
182 data=<link linkend="func-ref-comedi-from-phys">comedi_from_phy</link>s(it,volts,range,maxdata);
183 </programlisting>
184
185 </section>
186
187 <section id="usingfileinterface">
188 <title>
189 Using the file interface
190 </title>
191
192
193 <para>
194 In addition to providing low level routines for data
195 access, the &comedi; library provides higher-level access,
196 much like the standard <acronym>C</acronym> library provides
197 <function>fopen()</function>, etc.  as a high-level (and portable)
198 alternative to the direct <acronym>UNIX</acronym> system calls
199 <function>open()</function>, etc.  Similarily to
200 <function>fopen()</function>, we have
201 <link linkend="func-ref-comedi-open">comedi_open()</link>:
202 </para>
203
204 <programlisting>
205 file=<link linkend="func-ref-comedi-open">comedi_open</link>("/dev/comedi0");
206 </programlisting>
207
208 <para>
209 where <parameter class=function>file</parameter> is of type 
210 <parameter>(<link linkend="ref-type-comedi-t">comedi_t</link> *)</parameter>.
211 This function calls <function>open()</function>, as done explicitly in
212 a previous section, but also fills the
213 <link linkend="ref-type-comedi-t">comedi_t</link>
214 structure with lots of goodies; this information will be useful soon.  
215 </para>
216
217 <para>
218 Specifically, you need to know
219 <parameter class=function>maxdata</parameter> for a specific
220 subdevice/channel. How about:
221
222 <programlisting>
223 maxdata=<link linkend="func-ref-comedi-get-maxdata">comedi_get_maxdata</link>(file,subdevice,channel);
224 </programlisting>
225
226 Wow! How easy. And the range information?
227
228 <programlisting>
229 <link linkend="ref-type-comedi-range">comedi_range</link> * <link linkend="func-ref-comedi-get-range">comedi_get_range(<link linkend="ref-type-comedi-t">comedi_t</link>comedi_t</link> *it,unsigned int subdevice,unsigned int chan,unsigned int range);
230 </programlisting>
231
232 </para>
233
234 </section>
235
236
237 <section id="secondprogram">
238 <title>
239 Your second &comedi; program: simple acquisition
240 </title>
241
242
243 <para>
244 Actually, this is the first &comedi; program again, just
245 that we've added what we've learned.
246 </para>
247
248
249 <programlisting>
250 #include &lt;stdio.h&gt;      /* for printf() */
251 #include <![CDATA[<]]><link linkend="comedi-comedilib-h">comedilib.h</link><![CDATA[>]]>
252
253 int subdev = 0;         /* change this to your input subdevice */
254 int chan = 0;           /* change this to your channel */
255 int range = 0;          /* more on this later */
256 int aref = 0;           /* more on this later */
257
258 int main(int argc,char *argv[])
259 {
260   <link linkend="ref-type-comedi-t">comedi_t</link> *cf;
261   int chan=0;
262   <link linkend="ref-type-lsampl-t">lsampl_t</link> data;
263   int maxdata,rangetype;
264   double volts;
265
266   cf=<link linkend="func-ref-comedi-open">comedi_open</link>("/dev/comedi0");
267
268   maxdata=<link linkend="func-ref-comedi-get-maxdata">comedi_get_maxdata</link>(cf,subdev,chan);
269
270   rangetype=comedi_get_rangetype(cf,subdev,chan);
271
272   <link linkend="func-ref-comedi-data-read">comedi_data_read</link>(cf->fd,subdev,chan,range,aref,&amp;data);
273
274   volts=<link linkend="func-ref-comedi-to-phys">comedi_to_phys</link>(data,rangetype,range,maxdata);
275
276   printf("%d %g\n",data,volts);
277
278  return 0;
279 }
280 </programlisting>
281
282 </section>
283
284 <section id="thirdprogram">
285 <title>
286 Your third &comedi; program: instructions
287 </title>
288 <para>
289 This program (taken from the set of demonstration examples that come
290 with &comedi;) shows how to use a somewhat more flexible acquisition
291 function, the so-called <link linkend="instructions">instruction</link>.
292 <programlisting>
293 <![CDATA[
294 #include <stdio.h>
295 #include <]]><link linkend="comedi-comedilib-h">comedilib.h</link><![CDATA[>
296 #include <fcntl.h>
297 #include <unistd.h>
298 #include <errno.h>
299 #include <sys/time.h>
300 #include <unistd.h>
301 #include "examples.h"
302 ]]>
303
304 /*
305  * This example does 3 instructions in one system call.  It does
306  * a gettimeofday() call, then reads N_SAMPLES samples from an
307  * analog input, and the another gettimeofday() call.
308  */
309
310 #define MAX_SAMPLES 128
311
312 <link linkend="ref-type-comedi-t">comedi_t</link> *device;
313
314 int main(int argc, char *argv[])
315 {
316   int ret,i;
317   <link linkend="ref-type-comedi-insn">comedi_insn</link> insn[3];
318   <link linkend="ref-type-comedi-insnlist">comedi_insnlist</link> il;
319   struct timeval t1,t2;
320   <link linkend="ref-type-lsampl-t">lsampl_t</link> data[MAX_SAMPLES];
321
322   parse_options(argc,argv);
323
324
325   device=<link linkend="func-ref-comedi-open">comedi_open</link>(filename);
326   if(!device){
327     <link linkend="func-ref-comedi-perror">comedi_perror</link>(filename);
328     exit(0);
329   }
330
331   if(verbose){
332     printf("measuring device=%s subdevice=%d channel=%d range=%d analog reference=%d\n",
333       filename,subdevice,channel,range,aref);
334   }
335
336   /* Set up a the "instruction list", which is just a pointer
337    * to the array of instructions and the number of instructions.
338    */
339   il.n_insns=3;
340   il.insns=insn;
341
342   /* Instruction 0: perform a gettimeofday() */
343   insn[0].insn=<link linkend="insn-gtod">INSN_GTOD</link>;
344   insn[0].n=2;
345   insn[0].data=(void *)&amp;t1;
346
347   /* Instruction 1: do 10 analog input reads */
348   insn[1].insn=<link linkend="insn-read">INSN_READ</link>;
349   insn[1].n=n_scan;
350   insn[1].data=data;
351   insn[1].subdev=subdevice;
352   insn[1].chanspec=<link linkend="ref-macro-CR-PACK">CR_PACK</link>(channel,range,aref);
353
354   /* Instruction 2: perform a gettimeofday() */
355   insn[2].insn=<link linkend="insn-gtod">INSN_GTOD</link>;
356   insn[2].n=2;
357   insn[2].data=(void *)&amp;t2;
358
359   ret=<link linkend="func-ref-comedi-do-insnlist">comedi_do_insnlist</link>(device,&amp;il);
360   if(ret<![CDATA[<]]>0){
361     <link linkend="func-ref-comedi-perror">comedi_perror</link>(filename);
362     exit(0);
363   }
364
365   printf("initial time: %ld.%06ld\n",t1.tv_sec,t1.tv_usec);
366   for(i=0;i<![CDATA[<]]>n_scan;i++){
367     printf("%d\n",data[i]);
368   }
369   printf("final time: %ld.%06ld\n",t2.tv_sec,t2.tv_usec);
370
371   printf("difference (us): %ld\n",(t2.tv_sec-t1.tv_sec)*1000000+
372       (t2.tv_usec-t1.tv_usec));
373
374   return 0;
375 }
376 </programlisting>
377 </para>
378
379 </section>
380
381 <section id="fourthprogram">
382 <title>
383 Your fourth &comedi; program: commands
384 </title>
385
386 <para>
387 This example programs an analog output subdevice with &comedi;'s most
388 powerful acquisition function, the asynchronous
389 <link linkend="commandsstreaming">command</link>, to generate a waveform. 
390 </para>
391
392 <para>
393 The waveform in this example is a sine wave, but this can be easily
394 changed to make a generic function generator.
395 </para>
396
397 <para>
398 The function generation algorithm is the same as what is typically
399 used in digital function generators.  A 32-bit accumulator is
400 incremented by a phase factor, which is the amount (in radians) that
401 the generator advances each time step.  The accumulator is then
402 shifted right by 20 bits, to get a 12 bit offset into a lookup table.
403 The value in the lookup table at that offset is then put into a buffer
404 for output to the DAC.
405 </para>
406
407 <para>
408 Once you have
409 issued the command, &comedi; expects you to keep the buffer full of
410 data to output to the acquisition card.  This is done by 
411 <function>write()</function>.  Since there may be a delay between the 
412 <link linkend="func-ref-comedi-command">comedi_command()</link>
413 and a subsequent <function>write()</function>, you
414 should fill the buffer using <function>write()</function> before you call 
415 <link linkend="func-ref-comedi-command">comedi_command()</link>,
416 as is done here.
417 <programlisting>
418 <![CDATA[
419 #include <stdio.h>
420 #include <]]><link linkend="comedi-comedilib-h">comedilib.h</link><![CDATA[>
421 #include <fcntl.h>
422 #include <stdlib.h>
423 #include <unistd.h>
424 #include <errno.h>
425 #include <getopt.h>
426 #include <ctype.h>
427 #include <math.h>
428 #include "examples.h"
429 ]]>
430
431 double waveform_frequency = 10.0; /* frequency of the sine wave to output */
432 double amplitude          = 4000; /* peak-to-peak amplitude, in DAC units (i.e., 0-4095) */
433 double offset             = 2048; /* offset, in DAC units */
434
435 /* This is the size of chunks we deal with when creating and
436    outputting data.  This *could* be 1, but that would be
437    inefficient */
438 #define BUF_LEN 4096
439
440 int subdevice;
441 int external_trigger_number = 0;
442
443 sampl_t data[BUF_LEN];
444
445 void <link linkend="dds-output">dds_output</link>(sampl_t *buf,int n);
446 void <link linkend="dds-init">dds_init</link>(void);
447
448 /* This define determines which waveform to use. */
449 #define <anchor id="dds-init-function">dds_init_function <link linkend="dds-init-sine">dds_init_sine</link>
450
451 void <link linkend="dds-init-sine">dds_init_sine</link>(void);
452 void <link linkend="dds-init-pseudocycloid">dds_init_pseudocycloid</link>(void);
453 void <link linkend="dds-init-sawtooth">dds_init_sawtooth</link>(void);
454
455 int <anchor id="comedi-internal-trigger">comedi_internal_trigger(<link linkend="ref-type-comedi-t">comedi_t</link> *dev, unsigned int subd, unsigned int trignum)
456 {
457   <link linkend="ref-type-comedi-insn">comedi_insn</link> insn;
458   <link linkend="ref-type-lsampl-t">lsampl_t</link> data[1];
459
460   memset(<![CDATA[&insn]]>, 0, sizeof(<link linkend="ref-type-comedi-insn">comedi_insn</link>));
461   insn.insn = <link linkend="insn-inttrig">INSN_INTTRIG</link>;
462   insn.subdev = subd;
463   insn.data = data;
464   insn.n = 1;
465
466   data[0] = trignum;
467
468   return <link linkend="func-ref-comedi-do-insn">comedi_do_insn</link>(dev, <![CDATA[&insn]]>);
469 }
470
471
472 int main(int argc, char *argv[])
473 {
474   <link linkend="ref-type-comedi-cmd">comedi_cmd</link> cmd;
475   int err;
476   int n,m;
477   int total=0;
478   <link linkend="ref-type-comedi-t">comedi_t</link> *dev;
479   unsigned int chanlist[16];
480   unsigned int maxdata;
481   <link linkend="ref-type-comedi-range">comedi_range</link> *rng;
482   int ret;
483   <link linkend="ref-type-lsampl-t">lsampl_t</link> insn_data = 0; 
484
485   parse_options(argc,argv);
486
487   /* Force n_chan to be 1 */
488   n_chan = 2;
489
490   if(value){ waveform_frequency = value; }
491
492   dev = <link linkend="func-ref-comedi-open">comedi_open</link>(filename);
493   if(dev == NULL){
494     fprintf(stderr, "error opening %s\n", filename);
495     return -1;
496   }
497   subdevice = <link linkend="func-ref-comedi-find-subdevice-by-type">comedi_find_subdevice_by_type</link>(dev,COMEDI_SUBD_AO,0);
498
499   maxdata   = <link linkend="func-ref-comedi-get-maxdata">comedi_get_maxdata</link>(dev,subdevice,0);
500   rng       = <link linkend="func-ref-comedi-get-range">comedi_get_range</link>(dev,subdevice,0,0);
501   offset    = (double)<link linkend="func-ref-comedi-from-phys">comedi_from_phys</link>(0.0,rng,maxdata);
502   amplitude = (double)<link linkend="func-ref-comedi-from-phys">comedi_from_phys</link>(1.0,rng,maxdata) - offset;
503
504   memset(<![CDATA[&cmd]]>,0,sizeof(cmd));
505   /* fill in the <link linkend="ref-type-comedi-cmd">command data structure</link>: */
506   cmd.subdev         = subdevice;
507   cmd.flags          = 0;
508   cmd.start_src      = <link linkend="trig-int-start-src">TRIG_INT</link>;
509   cmd.start_arg      = 0;
510   cmd.scan_begin_src = <link linkend="trig-timer">TRIG_TIMER</link>;
511   cmd.scan_begin_arg = 1e9/freq;
512   cmd.convert_src    = <link linkend="trig-now">TRIG_NOW</link>;
513   cmd.convert_arg    = 0;
514   cmd.scan_end_src   = <link linkend="trig-count">TRIG_COUNT</link>;
515   cmd.scan_end_arg   = n_chan;
516   cmd.stop_src       = <link linkend="trig-none">TRIG_NONE</link>;
517   cmd.stop_arg       = 0;
518
519   cmd.chanlist       = chanlist;
520   cmd.chanlist_len   = n_chan;
521
522   chanlist[0] = <link linkend="ref-macro-CR-PACK">CR_PACK</link>(channel,range,aref);
523   chanlist[1] = <link linkend="ref-macro-CR-PACK">CR_PACK</link>(channel+1,range,aref);
524
525   <link linkend="dds-init">dds_init</link>();
526
527   <link linkend="dds-output">dds_output</link>(data,BUF_LEN);
528   <link linkend="dds-output">dds_output</link>(data,BUF_LEN);
529
530   dump_cmd(stdout,<![CDATA[&cmd]]>);
531
532   if ((err = <link linkend="func-ref-comedi-command">comedi_command</link>(dev, <![CDATA[&cmd]]>)) < 0) {
533     <link linkend="func-ref-comedi-perror">comedi_perror</link>("comedi_command");
534     exit(1);
535   }
536
537   m=write(comedi_fileno(dev),data,BUF_LEN*sizeof(sampl_t));
538   if(<![CDATA[m<0]]>){
539     perror("write");
540     exit(1);
541   }
542   printf("m=%d\n",m);
543   
544   ret = <link linkend="comedi-internal-trigger">comedi_internal_trigger</link>(dev, subdevice, 0);
545 <![CDATA[
546   if(ret<0){
547 ]]>
548     perror("comedi_internal_trigger\n");
549     exit(1);
550   }
551
552   while(1){
553     <link linkend="dds-output">dds_output</link>(data,BUF_LEN);
554     n=BUF_LEN*sizeof(sampl_t);
555     while(n>0){
556       m=write(comedi_fileno(dev),(void *)data+(BUF_LEN*sizeof(sampl_t)-n),n);
557 <![CDATA[
558       if(m<0){
559 ]]>
560         perror("write");
561         exit(0);
562       }
563       printf("m=%d\n",m);
564       n-=m;
565     }
566     total+=BUF_LEN;
567   }
568
569   return 0;
570 }
571
572 #define WAVEFORM_SHIFT 16
573 <![CDATA[
574 #define WAVEFORM_LEN (1<<WAVEFORM_SHIFT)
575 ]]>
576 #define WAVEFORM_MASK (WAVEFORM_LEN-1)
577
578 sampl_t waveform[WAVEFORM_LEN];
579
580 unsigned int acc;
581 unsigned int adder;
582
583 void <anchor id="dds-init">dds_init(void)
584 {
585 <![CDATA[
586   adder=waveform_frequency/freq*(1<<16)*(1<<WAVEFORM_SHIFT);
587 ]]>
588
589   <link linkend="dds-init-function">dds_init_function</link>();
590 }
591
592 void <anchor id="dds-output">dds_output(sampl_t *buf,int n)
593 {
594   int i;
595   sampl_t *p=buf;
596
597   <![CDATA[
598   for(i=0;i<n;i++){
599   *p=waveform[(acc>>16)&WAVEFORM_MASK];
600   ]]>
601   p++;
602   acc+=adder;
603   }
604 }
605
606
607 void <anchor id="dds-init-sine">dds_init_sine(void)
608 {
609   int i;
610
611   <![CDATA[
612   for(i=0;i<WAVEFORM_LEN;i++){
613   waveform[i]=rint(offset+0.5*amplitude*cos(i*2*M_PI/WAVEFORM_LEN));
614   ]]>
615   }
616 }
617
618 /* Yes, I know this is not the proper equation for a cycloid.  Fix it. */
619 void <anchor id="dds-init-pseudocycloid">dds_init_pseudocycloid(void)
620 {
621   int i;
622   double t;
623
624   <![CDATA[
625   for(i=0;i<WAVEFORM_LEN/2;i++){
626   t=2*((double)i)/WAVEFORM_LEN;
627   waveform[i]=rint(offset+amplitude*sqrt(1-4*t*t));
628   }
629   for(i=WAVEFORM_LEN/2;i<WAVEFORM_LEN;i++){
630   t=2*(1-((double)i)/WAVEFORM_LEN);
631   waveform[i]=rint(offset+amplitude*sqrt(1-t*t));
632   }
633   ]]>
634 }
635
636 void <anchor id="dds-init-sawtooth">dds_init_sawtooth(void)
637 {
638   int i;
639
640   <![CDATA[
641   for(i=0;i<WAVEFORM_LEN;i++){
642   waveform[i]=rint(offset+amplitude*((double)i)/WAVEFORM_LEN);
643   ]]>
644   }
645 }
646 </programlisting>
647 </para>
648
649 </section>
650
651 </section>
652