Skip to content

Instantly share code, notes, and snippets.

@enusbaum
Created March 9, 2018 12:22
Show Gist options
  • Select an option

  • Save enusbaum/eb40d1dcbf5d5630292ccf94b7daa45d to your computer and use it in GitHub Desktop.

Select an option

Save enusbaum/eb40d1dcbf5d5630292ccf94b7daa45d to your computer and use it in GitHub Desktop.
Oscillator Class in C# to pulse an AutoResetEvent at a sub-millisecond rate
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace TimerTest
{
/// <summary>
/// Oscillator class to pulse the specified AutoResetEvents at a higher frequency
/// than the minimum resolution of most system timers of 1ms
///
/// The Oscillator spins watching elapsed ticks from the Stopwatch object and pulses
/// the specified AutoResetEvents at the specified frequency.
///
/// On my i5 with a PulseRate of 50,000 it will come within 0.01%, not exactly within
/// the tolerances of a commercial Oscillator, but it works.
/// </summary>
public class Oscillator
{
private readonly Stopwatch _masterClock = new Stopwatch();
private readonly AutoResetEvent[] _targets = new AutoResetEvent[10];
private int _targetCount;
private readonly long _maxFrequency = Stopwatch.Frequency;
private long _pulseRate;
private long _ticksPerPulse;
private long _lastPulseTicks;
private bool _running;
/// <summary>
/// Number of Pulses-Per-Second
/// </summary>
public long PulseRate
{
get => _pulseRate;
set
{
if(value > _maxFrequency)
throw new Exception("Pulse Rate higher than maximum supported pulse rate");
_pulseRate = value;
_ticksPerPulse =_maxFrequency / _pulseRate;
Console.WriteLine($"{PulseRate} : {_ticksPerPulse} : {_maxFrequency}");
}
}
/// <summary>
/// Default Constructor
/// </summary>
public Oscillator()
{
if(!Stopwatch.IsHighResolution)
throw new Exception("Oscillator requres HPC be in use");
}
/// <summary>
/// Adds AutoResetEvent Target that will be Set during the Oscillator pulses
/// </summary>
/// <param name="target"></param>
public void AddTarget(AutoResetEvent target)
{
if (_running)
throw new Exception("Unable to add new targets while Oscillator is running");
if (_targetCount == _targets.Length)
throw new Exception("Max targets already specified, high precision not possible with multiple targets");
_targets[_targetCount] = target;
_targetCount++;
}
/// <summary>
/// Starts the Oscillator
/// </summary>
public void Start()
{
if(_targetCount == 0)
throw new Exception("At least one target must be specified before starting the Oscillator");
_running = true;
_masterClock.Start();
Task.Factory.StartNew(Oscillate, TaskCreationOptions.LongRunning);
}
/// <summary>
/// Stops the Oscillator
/// </summary>
public void Stop()
{
_running = false;
}
/// <summary>
/// Oscillator Pulse routine that runs within the Task created in Start()
/// </summary>
private void Oscillate()
{
while (_running)
{
_lastPulseTicks = _masterClock.ElapsedTicks;
for (int i = 0; i < _targetCount; i++)
_targets[i].Set();
while (_masterClock.ElapsedTicks - _lastPulseTicks < _ticksPerPulse) { }
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment