FastLED 3.10.8
Loading...
Searching...
No Matches

◆ update()

void fl::WaveSimulation1D_Real::update ( )

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

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

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

+ Here is the call graph for this function: