I found an impurity that I could not let stand.
The mirroring duplicates samples that shouldn't be duplicated. It is most egregious at the zero crossing -- a double sample of zero right where the sine should be most linear.
The fix is elegant. I just extended the dataset by 1 sample to be endpoint INCLUSIVE, then handled the mirroring asymmetrically to avoid duplicates. It actually cleans up the mirroring code, I think:
// Below, endpoint asymmetry assures non-overlapping quadrants if (index < quadLen) return _sin[index]; // 1st quadrant index -= quadLen; if (index < quadLen) return _sin[quadLen - index]; // 2nd quadrant index -= quadLen; if (index < quadLen) return -_sin[index]; // 3rd quadrant index -= quadLen; return -_sin[quadLen - index]; // 4th quadrant
I also made these changes:
1) Added two public constructors, one for default sine wave and one for arbitrary phase (in radians).
2) Replaced your bitwise AND with more general modulo (%, aka "remainder"), required for next item.
3) Refactored the byte array length to be arbitrary. Just change variable quadLen to any integer and the sine data will be stored in quadLen+1 bytes. I'm using quadLen=32 for my tiny AGENT screen.
using System;using Microsoft.SPOT;namespace AgentWatchSineWaves{ // Simple 8-bit sine table // Adapted From http://forums.netduino.com/index.php?/topic/9115-simple-sine-table/ // public class SineTable { const double Tau = 2.0 * System.Math.PI; // http://tauday.com/tau-manifesto 6.283185307179586476925286766559 ... const int quadLen = 32; // A quandrant is one quarter length of a full period const double amplitude = Byte.MaxValue; // Maximum byte value is 255 (byte is unsigned) public static readonly SineTable Sin = new SineTable(); public static readonly SineTable Cos = new SineTable(Tau / 4.0); private static byte[] _sin; private int _offset; public SineTable() { if (_sin == null) // one-time initialization { _sin = new byte[quadLen + 1]; // From 0 to quadLen INCLUSIVE double a = 0; // Angle in radians // Tau (one period) = 4 quadrants double da = Tau / (4.0 * quadLen); // 1st quadrant (0...Tau/4) for (int i = 0; i < _sin.Length; i++, a += da) _sin[i] = (byte)(amplitude * System.Math.Sin(a)); } } public SineTable(double phaseInRadians) : this() { phaseInRadians %= Tau; // Normalize to ±1 cycle if (phaseInRadians < 0.0) phaseInRadians += Tau; // Equivalent positive angle _offset = (int)(phaseInRadians/Tau * 4 * quadLen); } public int this[int index] { get { index += _offset; index %= 4 * quadLen; // Modulo limits index to 4 quadrants // Below, endpoint asymmetry assures non-overlapping quadrants if (index < quadLen) return _sin[index]; // 1st quadrant index -= quadLen; if (index < quadLen) return _sin[quadLen - index]; // 2nd quadrant index -= quadLen; if (index < quadLen) return -_sin[index]; // 3rd quadrant index -= quadLen; return -_sin[quadLen - index]; // 4th quadrant } } }}
- mbruner63 likes this