# waveform function generator functions (demo)

As a reference for a (part of) possible project I wrote a number of tunable waveform functions for sawtooth, triangle, square, sinus, stair. The sketch below print tab separated values to be shown e.g. in a spreadsheet.

The functions are not made or optimized for a real Arduino function generator however those functions could be derived from this implementation without too much trouble.

Typical function has the following signature:

double f(double t, double period, double amplitude, double phase, double yShift);

t: time (>=0)
period: period of signal
amplitude: positive and negative, an amplitude of 1 varies between -1 and 1 like sinus)
phase: offset of signal along time axis, typically 0 or between 0 and period
yShift: offset of signal along y-axis

the square and triangle have an additional dutyCycle param, stairs has an additional (int) steps parameter. All parameters except t have default values for easy usage.

``````//
//    FILE: functionGenerator.ino
//  AUTHOR: Rob Tillaart
// VERSION: 0.1.01
// PURPOSE: demo function generators
//    DATE: 2015-01-01
//     URL:
//
// Released to the public domain
//

double fgsaw(double t, double period = 1.0, double amplitude = 1.0, double phase = 0.0, double yShift = 0.0)
{
t += phase;
t = fmod(t, period);
return yShift + amplitude * (-1.0 + 2 * t / period);
}

double fgtri(double t, double period = 1.0, double amplitude = 1.0, double phase = 0.0, double yShift = 0.0, double dutyCycle = 0.50)
{
t += phase;
t = fmod(t, period);
// 50 % dutyCycle = faster
//  if (t * 2 < period) return yShift + amplitude * (-1.0 + 4 * t / period);
//  return yShift + amplitude * (3.0 - 4 * t / period);
if (t < dutyCycle * period) return yShift + amplitude * (-1.0 + 2 * t / (dutyCycle * period));
return yShift + amplitude * (-1.0 + 2 / (1 - dutyCycle) - 2 * t / ((1 - dutyCycle) * period));
}

double fgsqr(double t, double period = 1.0, double amplitude = 1.0, double phase = 0.0, double yShift = 0.0, double dutyCycle = 0.50)
{
t += phase;
t = fmod(t, period);
if (t < dutyCycle * period) return yShift + amplitude;
return yShift - amplitude;
}

double fgstr(double t, double period = 1.0, double amplitude = 1.0, double phase = 0.0, double yShift = 0.0, uint16_t steps = 8)
{
t += phase;
t = fmod(t, period);
int level = steps * t / period;
return yShift - amplitude + 2 * amplitude * level / (steps - 1);
}

double fgsin(double t, double period = TWO_PI, double amplitude = 1.0, double phase = 0.0, double yShift = 0.0)
{
t += phase;
double rv = amplitude * sin(TWO_PI * t / period) + yShift;
return rv;
}

uint32_t start;
uint32_t stop;

volatile double t;
volatile double y;

void setup()
{
Serial.begin(115200);
Serial.println("Start ");

Serial.println("func \t usec\t max/sec");
test_fgsqr();
delay(10);
test_fgsaw();
delay(10);
test_fgtri();
delay(10);
test_fgsin();
delay(10);
test_fgstr();
delay(10);
Serial.println();

Serial.println("t \t sqr\t saw\t tri\t sin\t str");
for (int i = 0; i < 500; i += 1)
{
// func(double t, double period = 1.0, double amplitude = 1.0, double phase = 0.0, double yShift = 0.0)
double t = i * 0.01;
Serial.print(t);
Serial.print("\t");
Serial.print(fgsqr(t));
Serial.print("\t");
Serial.print(fgsaw(t, 1.5));
Serial.print("\t");
Serial.print(fgtri(t, 1.0, 0.5, 1.0, 4.0));
Serial.print("\t");
Serial.print(fgsin(t, TWO_PI, 1.0, PI / 4)); // period 2PI, phase = 45°
// Serial.print(fgsin(t, 1.0, 0.5, -0.5, 0.5));
Serial.print("\t");
Serial.print(fgstr(t));
Serial.println();
}
Serial.println("\ndone...");
}

/******************************************************************/

void test_fgsqr()
{
start = micros();
for (int i = 0; i < 10000; i++) t = fgsqr(y);
stop = micros();
Serial.print("fsqr:\t");
Serial.print((stop - start) / 10000.0);
Serial.print("\t");
Serial.println(1000000.0 / ((stop - start) / 10000.0));
}

void test_fgsaw()
{
start = micros();
for (int i = 0; i < 10000; i++)
{
t = fgsaw(y);
}
stop = micros();
Serial.print("fsaw:\t");
Serial.print((stop - start) / 10000.0);
Serial.print("\t");
Serial.println(1000000.0 / ((stop - start) / 10000.0));
}

void test_fgtri()
{
start = micros();
for (int i = 0; i < 10000; i++)
{
t = fgtri(y);
}
stop = micros();
Serial.print("ftri:\t");
Serial.print((stop - start) / 10000.0);
Serial.print("\t");
Serial.println(1000000.0 / ((stop - start) / 10000.0));
}

void test_fgsin()
{
start = micros();
for (int i = 0; i < 10000; i++)
{
t = fgsin(y);
}
stop = micros();
Serial.print("fsin:\t");
Serial.print((stop - start) / 10000.0);
Serial.print("\t");
Serial.println(1000000.0 / ((stop - start) / 10000.0));
}

void test_fgstr()
{
start = micros();
for (int i = 0; i < 10000; i++)
{
t = fgstr(y);
}
stop = micros();
Serial.print("fstr:\t");
Serial.print((stop - start) / 10000.0);
Serial.print("\t");
Serial.println(1000000.0 / ((stop - start) / 10000.0));
}

void loop()
{
}

// END OF FILE
``````

todo’s: someday…

• negative values for t
• make a library of it
• use integer math
• make a real function generator
• added support for negative values, code increase quite a bit
• made the fmod conditional to increase performance a bit.

It would be better to have t constrained between zero and period, for performance and size sake.

``````double fgsaw(double t, double period = 1.0, double amplitude = 1.0, double phase = 0.0, double yShift = 0.0)
{
t += phase;
if (t >= 0)
{
if (t >= period) t = fmod(t, period);
return yShift + amplitude * (-1.0 + 2 * t / period);
}
t = -t;
if (t >= period) t = fmod(t, period);
return yShift + amplitude * ( 1.0 - 2 * t / period);
}

double fgtri(double t, double period = 1.0, double amplitude = 1.0, double phase = 0.0, double yShift = 0.0, double dutyCycle = 0.50)
{
t += phase;
if (t < 0) t = -t;
if (t >= period) t = fmod(t, period);
// 50 % dutyCycle = faster
//  if (t * 2 < period) return yShift + amplitude * (-1.0 + 4 * t / period);
//  return yShift + amplitude * (3.0 - 4 * t / period);
if (t < dutyCycle * period) return yShift + amplitude * (-1.0 + 2 * t / (dutyCycle * period));
return yShift + amplitude * (-1.0 + 2 / (1 - dutyCycle) - 2 * t / ((1 - dutyCycle) * period));
}

double fgsqr(double t, double period = 1.0, double amplitude = 1.0, double phase = 0.0, double yShift = 0.0, double dutyCycle = 0.50)
{
t += phase;
if (t >= 0)
{
if (t >= period) t = fmod(t, period);
if (t < dutyCycle * period) return yShift + amplitude;
return yShift - amplitude;
}
if (t >= period) t = fmod(-t, period);
if (t < dutyCycle * period) return yShift - amplitude;
return yShift + amplitude;
}

double fgsin(double t, double period = 1.0, double amplitude = 1.0, double phase = 0.0, double yShift = 0.0)
{
t += phase;
double rv = yShift + amplitude * sin(TWO_PI * t / period);
return rv;
}

double fgstr(double t, double period = 1.0, double amplitude = 1.0, double phase = 0.0, double yShift = 0.0, uint16_t steps = 8)
{
t += phase;
if (t >= 0)
{
if (t >= period) t = fmod(t, period);
int level = steps * t / period;
return yShift + amplitude * (-1.0 + 2.0 * level / (steps - 1));
}
t = -t;
if (t >= period) t = fmod(t, period);
int level = steps * t / period;
return yShift + amplitude * (1.0 - 2.0 * level / (steps - 1));
}
``````