FastLED 3.9.15
Loading...
Searching...
No Matches

◆ update()

void fl::WaveSimulation1D_Real::update ( )

Definition at line 127 of file wave_simulation_real.cpp.hpp.

127 {
128 i16 *curr = (whichGrid == 0) ? grid1.data() : grid2.data();
129 i16 *next = (whichGrid == 0) ? grid2.data() : grid1.data();
130
131 // Update boundaries with a Neumann (zero-gradient) condition:
132 curr[0] = curr[1];
133 curr[length + 1] = curr[length];
134
135 i32 mCourantSq32 = static_cast<i32>(mCourantSq);
136 // Hoist the lower-saturation bound: in half-duplex mode the new value
137 // must be >= 0 (negative parts of the wave are clipped); in full-duplex
138 // mode the full Q15 negative range is allowed. Folding this into the
139 // per-cell clamp via fl::clamp lets us drop the dedicated second pass
140 // that used to walk the whole grid after the update loop.
141 const i32 q15_min = mHalfDuplex ? 0 : -32768;
142 // Iterate over each inner cell.
143 for (fl::size i = 1; i < length + 1; i++) {
144 // Compute the 1D Laplacian:
145 // lap = curr[i+1] - 2 * curr[i] + curr[i-1]
146 i32 lap =
147 (i32)curr[i + 1] - ((i32)curr[i] << 1) + curr[i - 1];
148
149 // Multiply the Laplacian by the simulation speed using Q15 arithmetic.
150 // Promote to i64 before the multiply. With the 1D CFL clamp at 1.0,
151 // max |mCourantSq32| = 32767 and max |lap| ~131,070 (saturated
152 // alternating cells) give a worst-case product of ~4.3e9 — past
153 // i32 max (2.15e9). The i64 promote also acts as a safety net if
154 // the clamp is ever regressed; pre-clamp the same product could
155 // already reach ~4.3e9 in 1D and ~8.6e9 in 2D.
156 i32 term = static_cast<i32>(
157 (static_cast<i64>(mCourantSq32) * lap) >> 15);
158
159 // Compute the new value:
160 // f = -next[i] + 2 * curr[i] + term
161 i32 f = -(i32)next[i] + ((i32)curr[i] << 1) + term;
162
163 // Apply damping: use a precomputed Q15 multiplier in place of the
164 // arithmetic shift. Functionally equivalent for power-of-two damp
165 // (modulo 1-LSB rounding) but generalizes cleanly to non-power-of-
166 // two damping if the public API ever needs it. On Cortex-M4+ this
167 // lowers to a single smmlsr/smulwb; on AVR it's a 16x32 multiply
168 // (~30 cycles) — same ballpark as the shift but cleaner semantics.
169 f = static_cast<i32>(
170 (static_cast<i64>(f) * mDampDecayQ15) >> 15);
171
172 // Clamp f into [q15_min, 32767] in a single step. q15_min is 0 when
173 // half-duplex is on, -32768 otherwise. This subsumes both the Q15
174 // saturation clamp and the post-pass that used to zero negatives.
175 next[i] = static_cast<i16>(fl::clamp(f, q15_min, static_cast<i32>(32767)));
176 }
177
178 // Toggle the active grid.
179 whichGrid ^= 1;
180}
fl::i64 i64
Definition s16x16x4.h:222
constexpr enable_if< is_fixed_point< T >::value, T >::type clamp(T x, T lo, T hi) FL_NOEXCEPT

References fl::clamp(), grid1, grid2, length, mCourantSq, mDampDecayQ15, mHalfDuplex, and whichGrid.

+ Here is the call graph for this function: