{"id":13894209,"url":"https://github.com/RobTillaart/FunctionGenerator","last_synced_at":"2025-07-17T09:31:15.971Z","repository":{"id":45356556,"uuid":"271336817","full_name":"RobTillaart/FunctionGenerator","owner":"RobTillaart","description":"Arduino library to generate wave forms (nummeric) for a DAC","archived":false,"fork":false,"pushed_at":"2024-07-07T08:16:46.000Z","size":39,"stargazers_count":16,"open_issues_count":0,"forks_count":4,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-11-19T06:33:08.795Z","etag":null,"topics":["arduino","function-generator","waveform"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/RobTillaart.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"RobTillaart","custom":"https://www.paypal.me/robtillaart"}},"created_at":"2020-06-10T17:03:31.000Z","updated_at":"2024-09-26T05:40:04.000Z","dependencies_parsed_at":"2024-04-13T10:21:23.294Z","dependency_job_id":"a77190e3-07ef-40e9-986b-89e049cddce5","html_url":"https://github.com/RobTillaart/FunctionGenerator","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobTillaart%2FFunctionGenerator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobTillaart%2FFunctionGenerator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobTillaart%2FFunctionGenerator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobTillaart%2FFunctionGenerator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RobTillaart","download_url":"https://codeload.github.com/RobTillaart/FunctionGenerator/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226248201,"owners_count":17595158,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arduino","function-generator","waveform"],"created_at":"2024-08-06T18:01:26.529Z","updated_at":"2024-11-24T23:30:17.730Z","avatar_url":"https://github.com/RobTillaart.png","language":"C++","funding_links":["https://github.com/sponsors/RobTillaart","https://www.paypal.me/robtillaart"],"categories":["C++"],"sub_categories":[],"readme":"\n[![Arduino CI](https://github.com/RobTillaart/FunctionGenerator/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)\n[![Arduino-lint](https://github.com/RobTillaart/FunctionGenerator/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/FunctionGenerator/actions/workflows/arduino-lint.yml)\n[![JSON check](https://github.com/RobTillaart/FunctionGenerator/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/FunctionGenerator/actions/workflows/jsoncheck.yml)\n[![GitHub issues](https://img.shields.io/github/issues/RobTillaart/FunctionGenerator.svg)](https://github.com/RobTillaart/FunctionGenerator/issues)\n\n[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/FunctionGenerator/blob/master/LICENSE)\n[![GitHub release](https://img.shields.io/github/release/RobTillaart/FunctionGenerator.svg?maxAge=3600)](https://github.com/RobTillaart/FunctionGenerator/releases)\n[![PlatformIO Registry](https://badges.registry.platformio.org/packages/robtillaart/library/FunctionGenerator.svg)](https://registry.platformio.org/libraries/robtillaart/FunctionGenerator)\n\n\n# FunctionGenerator\n\nArduino library to generate (numeric) wave forms for a DAC.\n\n\n## Description\n\nThis library presents a class for a function generator in **software**. \nIt is typical used to control one or more DAC's.\nTo maximize signal quality one has to apply all (or most) processor power \nto calculate new values over and over again to get enough resolution. \nIn practice the generator is useful for low frequencies, \n0.01 - 25 Hz, depending on waveform and processor and number of DAC's.\n(see indication below).\n\nNote: this class generates float values, performance wise this can be optimized,\nto achieve higher speeds at cost of accuracy / precision.\n\nAs always, feedback and ideas are welcome.\n\n\n### Performance\n\nYou always have to verify your own performance measurements to see if \nyour requirements are met by this library.\n\n**Indication** of what performance can be expected (based upon 0.2.1 version).  \nNote that the values need to be transported to a DAC or serial port too.  \nNumbers based on performance example, for one single signal.\n\n\n|  Processor    |  Clock    |  Waveform  |  usec/call  |  max freq  | max values/period |\n|:--------------|----------:|:-----------|------------:|-----------:|--------:|\n|  Arduino UNO  |  16 MHz   |  sawtooth  |   62        |    60 Hz   |   268   |\n|  Arduino UNO  |  16 MHz   |  triangle  |   74        |    50 Hz   |   270   |\n|  Arduino UNO  |  16 MHz   |  square    |   53        |  1000 Hz   |    19   |\n|  Arduino UNO  |  16 MHz   |  sinus     |   164       |    25 Hz   |   152   |\n|  Arduino UNO  |  16 MHz   |  stair     |   81        |    50 Hz   |   246   |\n|  Arduino UNO  |  16 MHz   |  random    |   37        |  1000 Hz   |    27   |\n|  ESP32        |  240 MHz  |  sawtooth  |   3.8       |  1000 Hz   |   263   |\n|  ESP32        |  240 MHz  |  triangle  |   3.9       |  1000 Hz   |   256   |\n|  ESP32        |  240 MHz  |  square    |   2.8       |  1000 Hz   |   357   |\n|  ESP32        |  240 MHz  |  sinus     |   13.6      |   250 Hz   |   294   |\n|  ESP32        |  240 MHz  |  stair     |   4.8       |   800 Hz   |   260   |\n|  ESP32        |  240 MHz  |  random    |   1.3       |  1000 Hz   |   769   |\n\n\n- Assumption minimal around 250 samples per period to get a smooth signal.\n  If a rougher signal is OK, higher frequencies are possible.\n  For **square()** and **random()** less samples per period are often acceptable.\n- ESP32 can do more calculations however 1000 Hz seems to be a nice \n  upper limit for a software based function generator.\n- If one needs to control multiple DAC's one should divide the numbers\n  and round down to get an estimate.\n\n\nNote: hardware function generator https://github.com/RobTillaart/AD985X\n\n\nNote: 0.2.5 due to duty cycle the triangle and square \nhave become slightly slower.\n\n|  Processor    |  Clock    |  Waveform  |  usec/call  |  max freq  |\n|:--------------|----------:|:-----------|------------:|-----------:|\n|  Arduino UNO  |  16 MHz   |  triangle  |   84        |    50 Hz   |\n|  Arduino UNO  |  16 MHz   |  square    |   57        |  1000 Hz   |\n|  Arduino UNO  |  16 MHz   |  random_DC |   68        |   500 Hz   |\n\nSee **functionGeneratorPerformance.ino**\n\n\n### Accuracy\n\nIf the time parameter **t** grows large, the internal math may have rounding \nproblems after some time. This can and will affect the quality of the output.\nIt is advised to reset **t** after a number (e.g. 100) full periods\n\nNeeds further investigations.\n\n### Related\n\n- https://github.com/RobTillaart/AD9833 hardware waveform generator.\n- https://github.com/RobTillaart/AD985X hardware waveform generator.\n- https://github.com/RobTillaart/functionGenerator software waveform generator.\n- https://pages.mtu.edu/~suits/notefreqs.html frequency table for notes.\n\n\n## Interface\n\n```cpp\n#include \"functionGenerator.h\"\n```\n\n### Constructor\n\n- **funcgen(float period = 1.0, float amplitude = 1.0, float phase = 0.0, float yShift = 0.0)**\nAll parameters (except duty cycle) can be set in the constructor but also later in configuration.\nDefault dutyCycle is 50%.\n\n\n### Configuration\n\n- **void setPeriod(float period = 1.0)** set the period of the wave in seconds.\nThis is the inverse of the frequency.\n- **float getPeriod()** returns the set period.\n- **void setFrequency(float frequency = 1.0)** set the frequency of the wave in Hertz (1/s).\nThis is the inverse of the period.\n- **float getFrequency()** returns the set frequency in Hertz.\n- **void setAmplitude(float amplitude = 1.0)** sets the amplitude of the wave.\nThe range is from **-amplitude** to **+amplitude**.\nSetting the amplitude to 0 gives effectively a zero signal.\nSetting the amplitude to a negative value effectively inverts the signal.\n- **float getAmplitude()** returns the set amplitude.\n- **void setPhase(float phase = 0.0)** shifts the phase of the wave. \nWill only be noticeable when compared with other waves.\nPhase is also known as the X- or horizontal shift.\n- **float getPhase()** returns the set phase.\n- **void setYShift(float yShift = 0.0)** sets an Y-shift or vertical offset in amplitude.\nThis allows to set e.g. the zero level.\n- **float getYShift()** returns the set Y-shift.\n- **void setDutyCycle(float percentage = 100)** sets the duty cycle of the signal.\nExperimental, not all waveforms have a duty cycle or interpret it differently, see below.\nDuty cycle must be between 0 and 100% and will be clipped otherwise.\n- **float getDutyCycle()** returns the set (clipped) duty cycle.\n- **void setRandomSeed(uint32_t a, uint32_t b = 314159265)** sets the initial seeds for the\n(Marsaglia) random number generator. The first is mandatory, the second is optional.\n\n\n### Wave forms\n\nThe variable t == time in seconds.\n\n- **float sawtooth(float t, uint8_t mode = 0)** mode 0 is default.\n  - mode == 0 ==\u003e  sawtooth /|. \n  - mode == 1 ==\u003e sawtooth |\\\\. Effectively equals inverting the amplitude.\n- **float triangle(float t)** triangle form, duty cycle default 50%. \n- **float square(float t)** square wave with duty cycle default 50%.\n- **float sinus(float t)** sinus wave, has no duty cycle.\n- **float stair(float t, uint16_t steps = 8, uint8_t mode = 0)** defaults to 8 steps up.\n  - mode = 0 ==\u003e steps up\n  - mode = 1 ==\u003e steps down. Effectively equals inverting the amplitude.\n- **float random()** random noise generation between 0 and amplitude.\nUses Marsaglia random generator.\n- **float line()** constant voltage line. \nHeight depends on the YShift and amplitude.\n- **float zero()** constant zero.\n\nThe functions **line()** and **zero()** can be used to drive a constant voltage \nfrom a DAC and can be used to calibrate the generator / DAC combination.\n\n\nExperimental 0.2.7\n\n- **float sinusDiode(float t)** sinus wave, only positive pulses.  \n(better name welcome).\n- **float sinusRectified(float t)** sinus wave, with \"abs(negative pulses)\".  \n(better name welcome).\n- **float trapezium1(float t)** trapezium wave.\nDutyCycle changes steepness of the falling and rising edge.\nThe wave changes from a square wave, via trapezium to a triangle wave.\n- **float trapezium2(float t)** trapezium wave.\nDutyCycle changes duration HIGH vs LOW, wave stays trapezium like.\nNote at 50% DC the two trapezium functions are identical.\n- **float heartBeat(float t)** simplified heartbeat wave.\nTo get a regular BPM heartbeat one should **setFrequency(BPM/60.0)** e.g 72/60 = 1.2.\n- **float freeWave(float t, int16_t arr, int16_t N)** define a free wave form.\nIt uses an array of **N+1** values, dividing a full period in **N** equidistant steps.\nThe last value should equal the first value to have a smooth transition.\nThe values of the array normally vary between -10000 and +10000 to manage\nthe set the relative amplitude in small steps. \nThese are scaled back to -1.0 to +1.0 times the amplitude.\n\n\n### Duty Cycle \n\nSince 0.2.5 the library has **experimental** support for duty cycle.\nThe meaning of duty cycle differs per wave form.\nImplementation may change in the future.\n\nIn first iteration the following behaviour is implemented:\n\n- **square()** implements duty cycle in a well known way. \nAt the start of the period the signal goes \"HIGH\".\nAfter duty cycle % of the period the signal goes LOW until the end of the period.\n- **triangle()** function uses the duty cycle to shift the peak from the\nbegin (0%) to middle (50%) to end (100%) of the period. \n- **random_DC()** A duty cycle of 0% is no noise 100% is full amplitude noise \nwith respect to previous value. \nImplemented as a weighed average between new and previous value.\nMade a separate function as handling the duty cycle slows performance substantial.\nInitial starts at zero and can be adjusted with **YShift()**.\n- **float trapezium1(float t)** The duty cycle changes the steepness of the rising\nand falling edges. This changes the form from square wave to trapezium to triangle.\nThe length of the HIGH LOW level go from 0 to half a period.\n- **float trapezium2(float t)** The duty cycle determines the length of the HIGH level,\nwhich is 0 for 0% DC and half a period for 100% DC. \nThe rising and falling edges stay same.\n- **float heartBeat(float t)** The duty cycle determines the part of the period\nthat the signal ~zero.\n\n### No duty cycle\n\nThe other functions need to be investigated what duty cycle means.\nCurrent ideas that are **NOT** implemented:\n\n- **sawtooth()** - move from /|. to /|__  so 0% is a spike, 100% = normal.\nThink of it as the halve of the triangle wave.\n- **sinus()** move the two peaks like the triangle (adjusting steepness / freq)??\n- **stair()** like sawtooth??\n- **line()** has no period so does not make sense (yet).\n- **zero()** has no period so does not make sense (yet).\n- **float sinusDiode(float t)**\n- **float sinusRectified(float t)**\n- **float freeWave(float t, int16_t arr, int16_t N)**\n\n## Future\n\n#### Must\n\n- improve documentation\n  - reorganize\n  - section per function might be better.\n\n#### Should\n\n- smart reseed needed for random().\n  - initialize random generator with compile file + date + time.\n  - use function values for seed bits.\n- stand-alone functions in separate .h\n- clean up code\n\n#### Could\n\n- ASDR wave \n  - https://en.wikipedia.org/wiki/Envelope_(music)\n  - **float ADSR(float t, float A, float D, float S, float R)** \n  - ADSR are percentages, A + D + R \u003c 1.0\n  - S = % of amplitude.\n- external clock to synchronize two or more software function generators.\n- check for synergy with https://github.com/RobTillaart/AD985X\n- investigate performance.\n  - algorithms for DAC specific gains e.g. 10-12-16 bit.\n  - improve performance sin() lookup table.\n  - add float variable for ```_perDC = _period * _dutyCycle```\n  - do we need **freq4** ? not since DC.\n- heartBeat\n  - small noise/variation parameter on amplitude and frequency.\n  - reduce footprint ==\u003e wrapper around freeWave()\n- waves\n  - white noise, pink noise (better done with hardware)\n  - min() + max() =\u003e return +-amplitude + yshift?\n  - RC function curve.\n  - Gamma curve.\n- create a function table? with what?\n- create an example program to sample an arbitrary wave form\n  - output in the right format.\n  - slow sampling vs real time.\n\n\n#### Examples\n\n  - Amplitude modulation ?\n  - heartbeat curve?\n  - example ESP32 version as separate task.\n  - example with DAC. 8 12 16 bit.\n  - example with potentiometers for 4 parameters\n\n#### Wont\n\n- investigate duty cycle for waveforms\n  - Derived class for the duty cycle variants? or functions!\n  - **float squareDC()** performance (loss)\n  - **float triangleDC()**\n  - **float sawtoothDC()**\n  - **float sinusDC()** duty-cycle for sinus what does it mean. \n    - ==\u003e move peaks, two half sinus with diff frequency\n  - **float stairDC()**\n- Bezier curve? (too complex)\n- record a signal and play back  ==\u003e separate class\n- document max frequency per wave form etc.\n  Should this be in the library? differs per board.\n\n\n## Support\n\nIf you appreciate my libraries, you can support the development and maintenance.\nImprove the quality of the libraries by providing issues and Pull Requests, or\ndonate through PayPal or GitHub sponsors.\n\nThank you,\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRobTillaart%2FFunctionGenerator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FRobTillaart%2FFunctionGenerator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRobTillaart%2FFunctionGenerator/lists"}