A SOMEWHAT UNUSUAL APPROACH TO LUMPED ELEMENT MODELING: TIME DOMAIN IMPLEMENTATION OF COMPUTER-AIDED POLYNOMIAL SOLUTIONS TO LTI SYSTEMS VIA THE BILINEAR TRANSFORM (LUMPED ELEMENT TRANSDUCER EDITION)

INTRO

Imagine you want to simulate a speaker, utilizing Thiele-Small parameters. It’s traditional and “easy” to obtain a Frequency Response, but we live in the time domain, and you can’t hear a graph. I’ve stumbled into a method for directly translating any lumped element model of an LTI system into a set of filters which allow you to model the system’s response in time! Not only can we obtain the frequency response of a driver, but we can listen to it directly. Imagine being able to simulate a few speakers and select the one that sounds best!

In addition to an efficient means for auralization directly from the lumped-elements, I offer a unique solution to simulating the impedance of a loudspeaker.

THE LUMPED ELEMENTS

In the basic assumption of a lumped element model for a loudspeaker, where the 3 domains of a speaker are “analogized” to circuit elements, coupled via a gyrators (transformer shown due to Kicad limitations), the above circuit can be simulated (choose your poison) to examine transducer behavior in any of the three domains. The trick here is that the behavior of a speaker in the time domain is a mess of differential equations but circuit elements themselves have very nice solutions once you flip over to the frequency domain. Let’s start with a simple example.

A SIMPLE EXAMPLE AS A JUSTIFICATION FOR USING FREQUENCY DOMAIN

For example, for an RLC circuit ((R=0Ω for simplicity, resistance is not differential with frequency)

The time domain analysis looks something like this, where I is current and V is voltage.

written in keynote because LaTex

The main issue here is that to calculate current you must evaluate an expression which itself requires knowledge of the derivative of the current. This is a differential equation! It’s not impossible to compute this numerically (that is, assume a small δ and step through time little by little) but obtaining an “analytical” solution to the equation requires a special set of skills.

If instead you instead do some clever stuff with frequency, based on 1) Euler’s Identity and 2) the understanding from the Fourier Theorem that any signal may be represented by an infinite series of sine waves, there are some very handy and very useful solutions to differential equations. (warning: I’m using j notation for imaginary numbers)

Here’s my mini proof, where j is the square root of negative 1, ω is frequency, and t is time:

By taking advantage of the Euler identity and the very special property of e that it’s derivative is itself, we can evaluate the differential equations and reduce them to simple polynomial math (more on that later).

Since we now know the complex impedance of these circuit elements, the series circuit can be “solved” as such:

The final expression is a fully accurate solution to the circuit, and it tells us a lot about the behavior of the circuit. For instance: for a given current and frequency, increasing capacitance will lower voltage (the capacitor will proportionately charge less) and lowering frequency will increase voltage (capacitor will charge more!) but this is counteracted by the decreasing impeance of the inductor. Makes sense, but let’s visualize it to get the wholes story!

If the poison you so choose is a frequency domain analysis, you can easily plot the frequency domain response by substituting s = j*2*pi*f where f is an vector of sample points in Herz (e.g. f=[1,2,3…48kHz]). Here’s some simple Matlab code to do this:

f = 1:fs; %sample points
omega = 2*pi.*f; %convert to angular frequency
s = 1i*omega;
C=100e-6; %Farads
L=10e-4; %Henries
R=1; %Ohms
Z=1./(s*C)+s*L+R; %series circuit impedance

An interesting note before we go to the Laplace domain: the impedance of this circuit looks a lot like a graph of a quadratic function. In fact, if we replace with a variable…say s, it can be written as:

So, we’ve turned a differential equation into a polynomial equation!

BUT WHAT ABOUT TIME?

Bing, bang, boom! Frequency response of a lumped element circuit with an elegant polynomial solution! But what about time the time domain filter I promised? Armed with a frequency response (hitherto known as FR), it is certainly possible to create a time domain transfer function or filter. Here are some methods I’ve tried:

Methods to convert a Frequency Response (FR) to a time-domain filter

methodbig Opros, cons
IFFT(FR) → time domain convolution with signal O(m*n)
m is resolution of FR
n is signal samples
+ simple, rather direct
– accuracy limited by FR resolution
– super slow
DFT(signal) x FRO(m log n)+ relatively easy to understand and implement
– not “real-time” i.e. requires “frames” with overlap and add
– time domain artifacts due to finite signals, finite resolution
FIR2: Frequency sampling-based FIR filter designO(m log n)?+ brute force solution
– memory intensive filtering: very high order filter required for any reasonable accuracy (N==Fs)
– accuracy limited by FR resolution
filter coefficient optimization:
run steepest descent optimization on filter coefficients until filter matches FR
∞ for optimization (optimality not guaranteed)
O(n)* once filter is converged∞
*technically O(m*n) but filter order can be kept as low as order of system
+ kind of cool
+ extra brute force solution
– optimizing the filter is not a guaranteed-to-work process depending on FR complexity

None of these are especially satisfying to me because they’re all rather messy approximations with a few levels of abstraction. This is the meat and potatoes of this post. There are two handy dandy pieces of math we need: the Laplace transform, which converts from time domain to the complex frequency s-domain, and the bilinear transform which converts directly from the LaPlace s-domain equation to the discrete time domain of digital signals (airhorn noises). This is huge! We already did the frequency domain math for Rs, Ls, and Cs!

There’s just one thing: the Laplace domain is in terms of the complex frequency variable 𝑠 where 𝑠=𝜎+𝑗𝜔. We are allowed to convert from the Fourier domain to the LaPlace domain if we set 𝜎=0, which is more-or-less an assertion that the system is stable and the input is continuous (in time) and real (in frequency). Then we can simply replace every 𝑗𝜔 with an 𝑠:

This last expression is what is sometimes referred to as the polynomial solution. Let’s take that into the time domain:

Example code:

C=100e-6; %Farads
L=10e-4; %Henries
R=1; %Ohms

%polynomial expression
a=[L R 1/C] %denominator
b=[0 1 0] %numerator

[B,A]=bilinear(b,a,fs);

%results — this is all you need to filter any signal!
B = 0.0103 0.0000 -0.0103
A = 1.0000 -1.9751 0.9794

Checking the results shows alignment (of course!)

Ain’t that great? What’s great about this method is that this is the exact answer to “how will this circuit behave in response to a signal?” There are no cons (except for sampling frequency)!

methodbig Opros, cons
Bilinear transform of polynomial solutionO(n)*
technically m*n where m is the order of the system
+ it’s the exact solution
+ super fast
+ gives you F(f) and F(t)
+ sounds cool
– you have to “solve” the circuit

To bring it home, we can also filter any time signal with our RLC circuit, giving us the time response. Here’s a 10V stepped input:

Or a stepped sine wave:

Admittedly, the bilinear transform is a discrete time approximation of a continuous time object—and so is a digital signal— but in comparison, say to a the FIR2 approach, which requires, for reasonable levels of accuracy, a 48,000-term filter, I find it much more satisfying to have a 6-term filter derived directly from the physical properties of the circuit.

APPLYING THIS METHOD TO ACOUSTICS

Now that we have a method that’s copacetic for a simple example, let’s apply this to the electro-mechano-acoustic domain! This is where the cons come in. While the math is simple and exact, the algebra quickly becomes nightmarish for complex systems. A simple transducer model is relatively simple until you chase down the acoustic terms, and then it becomes hellish, but I have a solution for that, too. Observe:

Factoring this chonky conglomeration to obtain coefficients yields something so grotesque it cannot be displayed readably with LaTex:

But using the power of the symbolic library, we can easily collect the terms and calculate the coefficients:

Turning Z(s) into a time domain filter Z(t) is as simple as taking the bilinear transform of 1/Z again! The results are validated with a comparison to a full speaker simulation software. If you’re wondering where the orange line is, it’s directly beneath the yellow line.

Admittedly, the model with acoustic impedance added is very close to the one without, but we’re here for precision anyway!

Inverting to impedance:

Checking it against a real transducer (I switched TS parameters for this one to the ND91-4 by Dayton Audio, a classic mini-woofer)—overlaying the simulated response on top of the DATS measured impedance response (blue line) by Dayton Audio vs our orange polynomial line, it looks pretty damn close up to 1kHz! The loss off accuracy above 1k is most likely due to the secondary inductance/resistance of the magnetic circuit, which is not typically reported by manufacturers in the TS parameters or datasheets and therefore not modeled.

Et voila! with a little bit of computer assisted algebra, we can take a lumped element model and convert it into a time-domain filter to be applied directly to incoming signals. In this case, we solved the algebra to get us the time-domain impedance of a transducer model, but with some simple re-arranging and a little code we can convert voltage to excursion, SPL, velocity, transmitted force, and much more!

LIGHT WORK

When I was 6, my parents gave me my first disposable camera and I ran outside into the sunny Stanford afternoon and immediately took, in rapid succession, 26 blurry photos of a bush. I wanted pictures of it growing. For some unfortunate reason, I eventually grew up and I stopped paying attention to plants. I think you once told me—or gushed, most likely—that you had plants. I don’t remember specifically. I can only recognize the presence of plants by plant-shaped gaps in my memory. I can’t remember the actual plants themselves. That is, itself, a sign of those times.

But then, mid pandemic, I was suddenly re-beguiled by their verdant wiles. Plants are beautiful, delicate, mute repositories of life and yet they absolutely bustle. Quitely, they bustle. Sometimes I look at them and imagine how imperceptibly they are growing, with fragile inevitability. Even if I can’t see them grow, they are, without doubt, growing. They even represent sort of perceptual paradox for me: having observed them quite often, I am required to conclude that they are growing day over day, but I can never see them grow. Only when I am not looking. It fascinates me. Absolute dopamine factories, they are.

What’s also cool about plants is that they act as a reciprocal canary-vessel for self-care. Vessel: as they bring me oxygen and joy, taking care of them benefits me, and in the time I set aside to tend to them, I am required to chill the fuck out for a second. Canary: if I don’t chill the fuck out and tend to them, they wilt, which gives me an easy visual representation of my fraying sanity. In this way they embody and promote good vibes all around, which is dope. Plus, girls love plants. Basically, plants are lit.

But plants take like fooooorever to grow and while, yes, the last paragraph is all about the mindfulness of the green goobers, didyouknowplantsgodormantinthewinterwheretheymightnotgrowatall? That’s dumb. After some careful questioning of the local college kid who staffs the overpriced ~vibey~ nursery by my house, I figured out that the dormancy of the plants is only dependent on 3 factors: heat, humidity, and light/dark cycles. It wouldn’t feel right to engage in mindfulness without a little bit of optimization, right? And that’s how I got into making lamps. Plants need light. Loads of it. And so now my new thing is lamps, because they help plants grow, and also because this one girl from hinge said “like, isn’t everyone into plants now? But lamps are underrated.” Lamps are lit.

DESIGN

So the commission for this was a rescue grow-lamp for a Pothos stranded in a corner of a sterilely lit house my friends built themselves. Their house includes a lot of warm tones, light wood, and minimal design accents. Therefore, the key aspects of the lamp were: minimal, modern, super-warm, bright-but-indirect lighting (plants love bright-indirect) in the >800 lux region with a >1 foot light dispersal area.

I’ve seen a lot of halo lamps for grow lights; the circular arrangement is actually quite efficient for even light dispersal over a large area, but they always look a bit Christmas-kitch to me. I needed something to break up the IG lowest-common-denominator design and root it solidly in minimal post-modern, so I slapped some emergent complexity on the halo with easy-to-fab rectilinear slats.

Conceptual render

My major innovation in this space is using an infill-only slicing (0-wall gyroid infill support block @ 11% infill, thanks to Cura’s easy slicer interface) to create a fully solid yet translucent PLA shade.

Cura slicer preview

I paired this with the very warm-toned and very exciting fully compostable NonOlien PLA from Filamentum, printed hot (189°C) on the Ender 3 V2 (super affordable entry-tier printer, very easy to use). It came out smashingly, although at this temperature, the flow compensation and print accuracy was poor, so I had to undersize the zero-clearance interfaces by a whopping 370µm on each side. 5 hours of printing later, though, I had exactly what I had hoped for:

Test fit

The slats are 3 strips of 1×5″ poplar with the middle slat cut down to a 1.5″ relief, laminated longitudinally and then cut in 8mm strips transversely. I finished the poplar with 4 coats of Waterlox (soft wood is super thirsty) and a finish coat of semi-gloss Polycrylic, slapped some super warm high density 2700k LED strip lights in there and added a 2.1mm barrel connector for an easy 12V connection to an affordable 2A power supply (due to the 1.5m of installed LEDs). The results:

Stunning
Installed with the happy owner and happy plant—easy style!

FUCK YEAH RECURSION

A Function to Un-Nest Nested Structures in MATLAB

So I’m flying back from Shanghai right now, (youalreadyknow it’s business class) and I decided to business-expense-splurge for the in-flight wifi, which is about as fast as a snail in the process of peacefully passing away. This means the only scrap of entertainment I can garner from my laptop is writing code in MATLAB. Writing fuckaround code in a pretend programming language in a rickety IDE has a provided surprising source of succor; if you were here you’d know it too because you’d notice I have been sitting FOR HOURS WITHOUT MY SEATBELT ON because I was simply too enraptured by the alternating tides of hate and happiness that MATLAB so easily exerts on the user.

Basically, I’m working on a GUI that allows a user to programmatically  create test limits for test data, while also allowing the user to efficiently adjust the limits where our absurd test data manages to invalidate centuries of statistical knowledge. This requires allowing the user to load a specific test from within an absurdly packaged data file which comes from a test system written by someone who clearly much prefers obfuscating and hiding things over effective data collection WHICH in turn means deeply and variably nested structures. Like 400-megabyte-CSV, stuck-in-limbo-with-Leonardio-DiCaprio, booger-that-you-can-feel-but-after-several-embarrassing-red-lights-you-cannot-remove levels deep.

Therefore, after careful perusal of mathworks, I wrote the following code. It uses recursion to return an array of strings describing each and every branch of a struct, down to the twig. It is surely my greatest accomplishment thus far.

function allfields=fieldnamesr(struct);
% FIELDNAMESR recursively explores the depths of nested structs To find all
% possible branches
%      fuck yeah recursion
fields=fieldnames(struct); %are there any field names?
idx=1; %increment variable
for i=1:1:size(fields,1) %for all field names
    if isstruct(struct.(fields{i})) %see if that field name itself is a structure 
        temp=structRecurrr(struct.(fields{i})); %if also structure, then we must go deeper!
        for j=1:1:length(temp) %for all returned values 
        allfields{idx,1}=sprintf('%s.%s',fields{i},temp{j}); %return a string containing returned values
        idx=idx+1; %increment
        end
    else
        allfields{idx}=fields{i}; %if field name not struct, then we are at bottom level
        idx=idx+1; %increment
    end
end

PSR J1748-2446ad

Hey baby,

what’s the world coming to these days? It’s all going to hell isn’t it?
what if we just got away from it all? like light years away, like really out there
with the sky figuratively falling and with the economy and the people and the election and the general state of things

you’re the one thing that’s good in the world

so

I was thinking

let’s just get away from it all, like you’ve always wanted

you and me in a cottage on the sea side
you know? Let’s do something crazy and just run away
we don’t need anyone if we have each other.
Just you and me
I can see you’re with me to the end no matter what
so let’s elope,

let’s go some where crazy

I’ve got the perfect spot in mind:
let’s go to PSR J1748-2446ad
what if we did that? let’s just get away
I know this is a little out of the blue, a little crazy, I can see you’re a little worried, but it’ll be perfect

 

let me tell you about J1748-2446ad

it’s a little spot up north, in Sagittarius, by Terzan 5, it’s exactly the kind of place we’ve always imagined we’d grow old together in

there’ll be plenty of room for kids if we want to have some

it’s a fix me up for sure, the heating’s a little wacky, it’s probably like 6×10^5 Kelvin there year-round
but I love the way a little sweat looks on your skin

the foundation needs some work, the whole place might collapse into a black hole;
you’re all the foundation I’ve ever needed

it’s also a bit bright there; the 1.29×10^26 watts of emitted X-Rays would either instantly vaporize us or instantly give us cancer and then instantly vaporize us
I’m not worried about that though, your smile is pretty damn radiant and I haven’t vaporized yet, have I?

that aside, it’s got a great view, the perfect place to watch the sun set, though we might get a little motion sick because J1748-2446ad rotates at about 44,000 miles per hour. But you already spin me right round baby (like a record)
anyways, that’s 61,862,299 more sunsets a day we can watch together with our toes in the neutron sand

also probably the wifi won’t be that good, what with us moving at 0.25 the speed of light and the 2.3×10^13 Gauss magnetic field that will erase all of our devices

 

and also instantly destroy them

 

and also instantly crush our bodies

but I think it’s nice to unplug from everything every once in a while

Oh, and we’ve got to watch for the tide, or at least the tidal forces, because J1748-2446ad’s gravitational field,
well it’s about 106,387,117,347 G

it’ll keep us down to earth

but together I know we’d figure out how to make it work
and once we get there it’ll be our own little paradise
Imagine! what if we did it? what if we just got up and dropped everything and left right now?
with only high-energy astrophysics to bother you and I
we can finally have a cottage on the sea side like you always wanted

it’ll be just you and me

and a sea

of ultra-densely packed neutrons radiating gravitational waves into the starry sky

a place where we can just be us

just you and me
and PSR J1748-2446ad
I know its late at night, babe, but it’s 18,000 light years a way so we better get going
it’ll take 134,123,326 more anniversaries to get there if we sling shot by Jupiter

we can celebrate on the road