FastLED 3.10.6
Loading...
Searching...
No Matches
sin32.h
Go to the documentation of this file.
1#pragma once
2
3
4#include "fl/stl/int.h"
6#include "fastled_progmem.h"
7#include "fl/stl/noexcept.h"
8namespace fl {
9
10// Paired sin/cos quarter-wave LUT with interleaved values and derivatives.
11// Layout per entry [qi]: { y_sin(qi), m_sin(qi), y_cos(qi), m_cos(qi) }
12// where y_cos(qi) = y_sin(64-qi), m_cos(qi) = m_sin(64-qi).
13// 65 entries (indices 0..64), covering 0 to pi/2.
14// Full sine/cosine reconstructed via quarter-wave symmetry.
15// Quadratic interpolation with exact stored derivative for O(h^3) accuracy.
16//
17// Output range: [-2147418112, 2147418112] (= 32767 * 65536)
18// Stride: 4 i32 per entry (16 bytes). 65 entries = 1040 bytes.
19// Stored in FL_PROGMEM for AVR flash placement.
20extern const i32 FL_PROGMEM sinCosPairedLut[];
21
22struct SinCos32 {
25};
26
27// Read an i32 from the PROGMEM-qualified LUT.
29 return (i32)FL_PGM_READ_DWORD_ALIGNED(addr);
30}
31
32// Core branchless quadratic interpolation from paired LUT.
33// qi: quarter-wave table index (0..64)
34// qi_next: adjacent index (qi+1 for direct, qi-1 for mirrored)
35// dmask: 0x00000000 (direct) or 0xFFFFFFFF (mirrored, negates derivative)
36// t: fraction in [0, 65535]
37// offset: 0 for sin, 2 for cos (selects which pair within the stride-4 entry)
38FASTLED_FORCE_INLINE i32 sin32_interp(u8 qi, u8 qi_next, i32 dmask, u32 t, u8 offset = 0) FL_NO_EXCEPT {
39 i32 y0 = read_sin32_lut(&sinCosPairedLut[qi * 4 + offset]);
40 i32 m0 = read_sin32_lut(&sinCosPairedLut[qi * 4 + offset + 1]);
41 i32 y1 = read_sin32_lut(&sinCosPairedLut[qi_next * 4 + offset]);
42
43 // Branchless conditional negate derivative
44 m0 = (m0 ^ dmask) - dmask;
45
46 // Quadratic interpolation (Horner form, 2 muls):
47 // P(t) = y0 + T*(m0 + T*(y1 - y0 - m0)) where T = t/65536
48 i32 c = y1 - y0 - m0;
49 i32 r = (i32)((i64)c * t >> 16) + m0;
50 return (i32)(((i64)r * t >> 16) + (i64)y0);
51}
52
53// 0 to 16777216 is a full circle
54// output is between -2147418112 and 2147418112
55// Branchless quarter-wave lookup with quadratic interpolation.
56// Cost: 3 table loads, 2 i64 multiplies, no branches.
58 u8 angle256 = static_cast<u8>(angle >> 16); // 0..255
59 u32 t = angle & 0xFFFF; // 0..65535
60
61 u8 quadrant = angle256 >> 6; // 0..3
62 u8 pos = angle256 & 0x3F; // 0..63
63
64 // Branchless quarter-wave mapping
65 u8 mirror = quadrant & 1;
66 u8 qi = static_cast<u8>(pos + mirror * (64 - 2 * pos));
67 u8 qi_next = static_cast<u8>(qi + 1 - 2 * mirror);
68
69 i32 dmask = -static_cast<i32>(mirror);
70 i32 raw = sin32_interp(qi, qi_next, dmask, t);
71
72 // Branchless sign: negative in quadrants 2, 3
73 i32 vmask = -static_cast<i32>((quadrant >> 1) & 1);
74 return (i32)(((i64)raw ^ vmask) - vmask);
75}
76
77// 0 to 16777216 is a full circle
78// output is between -2147418112 and 2147418112
80 return sin32(angle + 4194304u);
81}
82
83// Compute sin and cos simultaneously, faster than separate sin32+cos32 calls.
84// Uses paired LUT: sin and cos data colocated at same index (no qi_c computation).
85// Cost: 6 table loads, 4 i64 multiplies, no branches.
87 u8 angle256 = static_cast<u8>(angle >> 16);
88 u32 t = angle & 0xFFFF;
89
90 u8 quadrant = angle256 >> 6;
91 u8 pos = angle256 & 0x3F;
92
93 // Quarter-wave mapping (same for both sin and cos)
94 u8 mirror_s = quadrant & 1;
95 u8 qi = static_cast<u8>(pos + mirror_s * (64 - 2 * pos));
96 u8 qi_next = static_cast<u8>(qi + 1 - 2 * mirror_s);
97
98 // Derivative masks: sin and cos have opposite mirror states
99 i32 sdmask = -static_cast<i32>(mirror_s);
100 i32 cdmask = ~sdmask; // opposite mirror
101
102 // Sin at offset 0, cos at offset 2 — same qi, same cache line
103 i32 s_raw = sin32_interp(qi, qi_next, sdmask, t, 0);
104 i32 c_raw = sin32_interp(qi, qi_next, cdmask, t, 2);
105
106 // Sin sign: negative in quadrants 2, 3
107 i32 svmask = -static_cast<i32>((quadrant >> 1) & 1);
108 // Cos sign: negative in quadrants 1, 2 (XOR of quadrant bits)
109 i32 cvmask = -static_cast<i32>((quadrant ^ (quadrant >> 1)) & 1);
110
111 SinCos32 out;
112 out.sin_val = (i32)(((i64)s_raw ^ svmask) - svmask);
113 out.cos_val = (i32)(((i64)c_raw ^ cvmask) - cvmask);
114 return out;
115}
116
117// 0 to 65536 is a full circle
118// output is between -32767 and 32767
120 u32 angle32 = static_cast<u32>(angle) << 8;
121 return static_cast<i16>(sin32(angle32) >> 16);
122}
123
124// 0 to 65536 is a full circle
125// output is between -32767 and 32767
127 u32 angle32 = static_cast<u32>(angle) << 8;
128 return static_cast<i16>(cos32(angle32) >> 16);
129}
130
131} // namespace fl
uint8_t pos
Definition Blur.ino:11
#define FASTLED_FORCE_INLINE
fl::UISlider offset("Offset", 0.0f, 0.0f, 1.0f, 0.01f)
#define FL_PGM_READ_DWORD_ALIGNED(addr)
Wrapper definitions to allow seamless use of PROGMEM in environments that have it.
#define FL_PROGMEM
PROGMEM keyword for storage.
FASTLED_FORCE_INLINE i32 sin32(u32 angle) FL_NO_EXCEPT
Definition sin32.h:57
unsigned char u8
Definition stdint.h:131
const i32 sinCosPairedLut[]
FASTLED_FORCE_INLINE i16 cos16lut(u16 angle) FL_NO_EXCEPT
Definition sin32.h:126
FASTLED_FORCE_INLINE SinCos32 sincos32(u32 angle) FL_NO_EXCEPT
Definition sin32.h:86
FASTLED_FORCE_INLINE i32 cos32(u32 angle) FL_NO_EXCEPT
Definition sin32.h:79
InputGamut g FL_NO_EXCEPT
Definition rgbw.h:121
FASTLED_FORCE_INLINE i32 read_sin32_lut(const i32 *addr) FL_NO_EXCEPT
Definition sin32.h:28
FASTLED_FORCE_INLINE i16 sin16lut(u16 angle) FL_NO_EXCEPT
Definition sin32.h:119
FASTLED_FORCE_INLINE i32 sin32_interp(u8 qi, u8 qi_next, i32 dmask, u32 t, u8 offset=0) FL_NO_EXCEPT
Definition sin32.h:38
Base definition for an LED controller.
Definition crgb.hpp:179
i32 sin_val
Definition sin32.h:23
i32 cos_val
Definition sin32.h:24
fl::i64 i64
Definition stdint.h:221