/ src / hal / components / siggen.c
siggen.c
  1  /********************************************************************
  2  * Description:  siggen.c
  3  *               This file, 'siggen.c', is a HAL component that 
  4  *               generates square, triangle, sine, cosine, and 
  5  *               sawtooth waves plus a clock signal.
  6  *
  7  * Author: John Kasunich
  8  * License: GPL Version 2
  9  *    
 10  * Copyright (c) 2003 All rights reserved.
 11  *
 12  * Last change:  17Nov2010 - Matt Shaver added the "clock" output pin.
 13  ********************************************************************/
 14  /** This file, 'siggen.c', is a HAL component that generates square,
 15      triangle, sine, cosine, and sawtooth waves.  I expect that it 
 16      will mostly be used for testing.  It is a realtime component.
 17  
 18      It supports any number of signal generators, as set by the
 19      insmod parameter 'num_chan'.  Alternatively,use the names= specifier
 20      and a list of unique names separated by commas.  The names= and
 21      num_chan= specifiers are mututally exclusive.
 22  
 23      Each generator has a number of pins and parameters, whose
 24      names begin with 'siggen.x.', where 'x' is the generator number.
 25      Generator numbers start at zero.
 26  
 27      Each generator is controlled by three pins.  'frequency' sets
 28      the frequency in Hertz.  'amplitude' sets the peak amplitude,
 29      and 'offset' sets the DC offset.  For example, if 'amplitude'
 30      is 1.0 and 'offset' is 0.0, the outputs will swing from -1.0
 31      to +1.0.  If 'amplitude' is 2.5 and 'offset' is 10.0, then
 32      the outputs will swing from 7.5 to 12.5.
 33  
 34      There are six output pins: 'square', 'triangle', 'sine', 'cosine',
 35      'clock', and 'sawtooth'.  All six run at the same frequency,
 36      amplitude, and offset.
 37  
 38      This component exports one function per signal generator,
 39      called 'siggen.x.update'.  It is a floating point function.
 40  
 41  */
 42  
 43  /** Copyright (C) 2003 John Kasunich
 44                         <jmkasunich AT users DOT sourceforge DOT net>
 45  */
 46  
 47  /** This program is free software; you can redistribute it and/or
 48      modify it under the terms of version 2 of the GNU General
 49      Public License as published by the Free Software Foundation.
 50      This library is distributed in the hope that it will be useful,
 51      but WITHOUT ANY WARRANTY; without even the implied warranty of
 52      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 53      GNU General Public License for more details.
 54  
 55      You should have received a copy of the GNU General Public
 56      License along with this library; if not, write to the Free Software
 57      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 58  
 59      THE AUTHORS OF THIS LIBRARY ACCEPT ABSOLUTELY NO LIABILITY FOR
 60      ANY HARM OR LOSS RESULTING FROM ITS USE.  IT IS _EXTREMELY_ UNWISE
 61      TO RELY ON SOFTWARE ALONE FOR SAFETY.  Any machinery capable of
 62      harming persons must have provisions for completely removing power
 63      from all motors, etc, before persons enter any danger area.  All
 64      machinery must be designed to comply with local and national safety
 65      codes, and the authors of this software can not, and do not, take
 66      any responsibility for such compliance.
 67  
 68      This code was written as part of the EMC HAL project.  For more
 69      information, go to www.linuxcnc.org.
 70  */
 71  
 72  #include "rtapi.h"		/* RTAPI realtime OS API */
 73  #include "rtapi_app.h"		/* RTAPI realtime module decls */
 74  #include "hal.h"		/* HAL public API decls */
 75  #include <float.h>
 76  #include <rtapi_math.h>
 77  #include <rtapi_string.h>
 78  
 79  /* module information */
 80  MODULE_AUTHOR("John Kasunich");
 81  MODULE_DESCRIPTION("Signal Generator Component for EMC HAL");
 82  MODULE_LICENSE("GPL");
 83  static int num_chan;	/* number of channels */
 84  static int default_num_chan = 1;
 85  static int howmany;
 86  RTAPI_MP_INT(num_chan, "number of channels");
 87  
 88  #define MAX_CHAN 16
 89  static char *names[MAX_CHAN] = {0,};
 90  RTAPI_MP_ARRAY_STRING(names, MAX_CHAN, "names of siggen");
 91  
 92  /***********************************************************************
 93  *                STRUCTURES AND GLOBAL VARIABLES                       *
 94  ************************************************************************/
 95  
 96  /** This structure contains the runtime data for a single siggen.
 97  */
 98  
 99  typedef struct {
100      hal_float_t *square;	/* pin: output */
101      hal_float_t *sawtooth;	/* pin: output */
102      hal_float_t *triangle;	/* pin: output */
103      hal_float_t *sine;		/* pin: output */
104      hal_float_t *cosine;	/* pin: output */
105      hal_bit_t *clock;		/* pin: output */
106      hal_float_t *frequency;	/* pin: frequency */
107      hal_float_t *amplitude;	/* pin: amplitude */
108      hal_float_t *offset;	/* pin: offset */
109      hal_bit_t *reset;		/* pin: reset */
110      double index;		/* position within output cycle */
111  } hal_siggen_t;
112  
113  /* pointer to array of siggen_t structs in shared memory, 1 per gen */
114  static hal_siggen_t *siggen_array;
115  
116  /* other globals */
117  static int comp_id;		/* component ID */
118  
119  /***********************************************************************
120  *                  LOCAL FUNCTION DECLARATIONS                         *
121  ************************************************************************/
122  
123  static int export_siggen(int num, hal_siggen_t * addr,char* prefix);
124  static void calc_siggen(void *arg, long period);
125  
126  /***********************************************************************
127  *                       INIT AND EXIT CODE                             *
128  ************************************************************************/
129  
130  
131  int rtapi_app_main(void)
132  {
133      int n, retval, i;
134  
135      if(num_chan && names[0]) {
136          rtapi_print_msg(RTAPI_MSG_ERR,"num_chan= and names= are mutually exclusive\n");
137          return -EINVAL;
138      }
139      if(!num_chan && !names[0]) num_chan = default_num_chan;
140  
141      if(num_chan) {
142          howmany = num_chan;
143      } else {
144          howmany = 0;
145          for (i = 0; i < MAX_CHAN; i++) {
146              if ( (names[i] == NULL) || (*names[i] == 0) ){
147                  break;
148              }
149              howmany = i + 1;
150          }
151      }
152  
153      /* test for number of channels */
154      if ((howmany <= 0) || (howmany > MAX_CHAN)) {
155  	rtapi_print_msg(RTAPI_MSG_ERR,
156  	    "SIGGEN: ERROR: invalid number of channels: %d\n", howmany);
157  	return -1;
158      }
159      /* have good config info, connect to the HAL */
160      comp_id = hal_init("siggen");
161      if (comp_id < 0) {
162  	rtapi_print_msg(RTAPI_MSG_ERR, "SIGGEN: ERROR: hal_init() failed\n");
163  	return -1;
164      }
165      /* allocate shared memory for siggen data */
166      siggen_array = hal_malloc(howmany * sizeof(hal_siggen_t));
167      if (siggen_array == 0) {
168  	rtapi_print_msg(RTAPI_MSG_ERR,
169  	    "SIGGEN: ERROR: hal_malloc() failed\n");
170  	hal_exit(comp_id);
171  	return -1;
172      }
173      /* export variables and functions for each siggen */
174      i = 0; // for names= items
175      for (n = 0; n < howmany; n++) {
176  	/* export everything for this loop */
177          if(num_chan) {
178              char buf[HAL_NAME_LEN + 1];
179              rtapi_snprintf(buf, sizeof(buf), "siggen.%d", n);
180  	    retval = export_siggen(n, &(siggen_array[n]),buf);
181          } else {
182  	    retval = export_siggen(n, &(siggen_array[n]),names[i++]);
183          }
184  
185  	if (retval != 0) {
186  	    rtapi_print_msg(RTAPI_MSG_ERR,
187  		"SIGGEN: ERROR: siggen %d var export failed\n", n);
188  	    hal_exit(comp_id);
189  	    return -1;
190  	}
191      }
192      rtapi_print_msg(RTAPI_MSG_INFO,
193  	"SIGGEN: installed %d signal generators\n", howmany);
194      hal_ready(comp_id);
195      return 0;
196  }
197  
198  void rtapi_app_exit(void)
199  {
200      hal_exit(comp_id);
201  }
202  
203  /***********************************************************************
204  *                       REALTIME LOOP CALCULATIONS                     *
205  ************************************************************************/
206  
207  static void calc_siggen(void *arg, long period)
208  {
209      hal_siggen_t *siggen;
210      double tmp1, tmp2;
211  
212      /* point to the data for this signal generator */
213      siggen = arg;
214      /* calculate the time since last execution */
215      tmp1 = period * 0.000000001;
216  
217      /* calculate how much of an output cycle that has passed */
218      tmp2 = *(siggen->frequency) * tmp1;
219      /* limit frequency to comply with Nyquist limit */
220      if ( tmp2 > 0.5 ) {
221  	*(siggen->frequency) = 0.5 / tmp1;
222  	tmp2 = 0.5;
223      }
224      /* index ramps from 0.0 to 0.99999 for each output cycle */
225      if ( *(siggen->reset) ) {
226  	siggen->index  = 0.5;
227      } else {
228  	siggen->index += tmp2;
229      }
230      /* wrap index if it is >= 1.0 */
231      if ( siggen->index >= 1.0 ) {
232  	siggen->index -= 1.0;
233      }
234  
235      /* generate the square wave and clock output */
236      /* tmp1 steps from -1.0 to +1.0 when index passes 0.5 */
237      if ( siggen->index > 0.5 ) {
238  	tmp1 = 1.0;
239  	*(siggen->clock) = 1;
240      } else {
241  	tmp1 = -1.0;
242  	*(siggen->clock) = 0;
243      }
244      /* apply scaling and offset, and write to output */
245      *(siggen->square) = (tmp1 * *(siggen->amplitude)) + *(siggen->offset);
246  
247      /* generate the sawtooth wave output */
248      /* tmp2 ramps from -1.0 to +1.0 as index goes from 0 to 1 */
249      tmp2 = (siggen->index * 2.0) - 1.0;
250      /* apply scaling and offset, and write to output */
251      *(siggen->sawtooth) = (tmp2 * *(siggen->amplitude)) + *(siggen->offset);
252  
253      /* generate the triangle wave output */
254      /* tmp2 ramps from -2.0 to +2.0 as index goes from 0 to 1 */
255      tmp2 *= 2.0;
256      /* flip first half of ramp, now goes from +1 to -1 to +1 */
257      tmp2 = (tmp2 * tmp1) - 1.0;
258      /* apply scaling and offset, and write to output */
259      *(siggen->triangle) = (tmp2 * *(siggen->amplitude)) + *(siggen->offset);
260  
261      /* generate the sine wave output */
262      /* tmp1 is angle in radians */
263      tmp1 = siggen->index * (2.0 * 3.1415927);
264      /* get sine, apply scaling and offset, and write to output */
265      *(siggen->sine) = (sin(tmp1) * *(siggen->amplitude)) + *(siggen->offset);
266  
267      /* generate the cosine wave output */
268      /* get cosine, apply scaling and offset, and write to output */
269      *(siggen->cosine) = (cos(tmp1) * *(siggen->amplitude)) + *(siggen->offset);
270      /* done */
271  }
272  
273  /***********************************************************************
274  *                   LOCAL FUNCTION DEFINITIONS                         *
275  ************************************************************************/
276  
277  static int export_siggen(int num, hal_siggen_t * addr,char* prefix)
278  {
279      int retval;
280      char buf[HAL_NAME_LEN + 1];
281  
282      /* export pins */
283      retval = hal_pin_float_newf(HAL_OUT, &(addr->square), comp_id,
284  				"%s.square", prefix);
285      if (retval != 0) {
286  	return retval;
287      }
288      retval = hal_pin_float_newf(HAL_OUT, &(addr->sawtooth), comp_id,
289  				"%s.sawtooth", prefix);
290      if (retval != 0) {
291  	return retval;
292      }
293      retval = hal_pin_float_newf(HAL_OUT, &(addr->triangle), comp_id,
294  				"%s.triangle", prefix);
295      if (retval != 0) {
296  	return retval;
297      }
298      retval = hal_pin_float_newf(HAL_OUT, &(addr->sine), comp_id,
299  				"%s.sine", prefix);
300      if (retval != 0) {
301  	return retval;
302      }
303      retval = hal_pin_float_newf(HAL_OUT, &(addr->cosine), comp_id,
304  				"%s.cosine", prefix);
305      if (retval != 0) {
306  	return retval;
307      }
308      retval = hal_pin_bit_newf(HAL_OUT, &(addr->clock), comp_id,
309  				"%s.clock", prefix);
310      if (retval != 0) {
311  	return retval;
312      }
313      retval = hal_pin_float_newf(HAL_IN, &(addr->frequency), comp_id,
314  				"%s.frequency", prefix);
315      if (retval != 0) {
316  	return retval;
317      }
318      retval = hal_pin_float_newf(HAL_IN, &(addr->amplitude), comp_id,
319  				"%s.amplitude", prefix);
320      if (retval != 0) {
321  	return retval;
322      }
323      retval = hal_pin_float_newf(HAL_IN, &(addr->offset), comp_id,
324  				"%s.offset", prefix);
325      if (retval != 0) {
326  	return retval;
327      }
328      retval = hal_pin_bit_newf(HAL_IN, &(addr->reset), comp_id,
329  				"%s.reset", prefix);
330      if (retval != 0) {
331  	return retval;
332      }
333      /* init all structure members */
334      *(addr->square) = 0.0;
335      *(addr->sawtooth) = 0.0;
336      *(addr->triangle) = 0.0;
337      *(addr->sine) = 0.0;
338      *(addr->cosine) = 0.0;
339      *(addr->clock) = 0;
340      *(addr->frequency) = 1.0;
341      *(addr->amplitude) = 1.0;
342      *(addr->offset) = 0.0;
343      addr->index = 0.0;
344      /* export function for this loop */
345      rtapi_snprintf(buf, sizeof(buf), "%s.update", prefix);
346      retval =
347  	hal_export_funct(buf, calc_siggen, &(siggen_array[num]), 1, 0,
348  	comp_id);
349      if (retval != 0) {
350  	rtapi_print_msg(RTAPI_MSG_ERR,
351  	    "SIGGEN: ERROR: update funct export failed\n");
352  	hal_exit(comp_id);
353  	return -1;
354      }
355      return 0;
356  }