-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsimulation.h
More file actions
247 lines (192 loc) · 7.73 KB
/
simulation.h
File metadata and controls
247 lines (192 loc) · 7.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
#pragma once
#include <memory>
#include <array>
#include <algorithm>
#include <cmath>
namespace MKAudio
{
// ==========================================
// 1. Optimized Component Classes
// ==========================================
class Component {
protected:
int nodeA, nodeB;
public:
Component(int n1, int n2);
virtual ~Component() = default;
// 1. Preprocess Step: Contributes to the static Y Matrix
virtual double getConductance(double dt) const = 0;
// 2. Real-time Step: Contributes to the dynamic J Vector
virtual double getCurrentSource(double dt) const = 0;
// 3. Post-Process Step: Updates internal memory
virtual void updateState(double vA, double vB, double dt) = 0;
// 4. Check if component value can change dynamically
virtual bool isDynamic() const;
int getNodeA() const;
int getNodeB() const;
};
class Resistor : public Component {
double conductance;
public:
Resistor(int n1, int n2, double r);
double getConductance(double dt) const override;
double getCurrentSource(double dt) const override;
void updateState(double vA, double vB, double dt) override;
void setResistance(double r);
double getResistance() const;
};
class Capacitor : public Component {
double capacitance;
double prevVoltage;
public:
Capacitor(int n1, int n2, double c);
double getConductance(double dt) const override;
double getCurrentSource(double dt) const override;
void updateState(double vA, double vB, double dt) override;
};
class Inductor : public Component {
double inductance;
double prevCurrent;
public:
Inductor(int n1, int n2, double l);
double getConductance(double dt) const override;
double getCurrentSource(double dt) const override;
void updateState(double vA, double vB, double dt) override;
};
// ==========================================
// Switch Class (2-way SPST Switch)
// ==========================================
// A switch is modeled as a variable resistor:
// - Closed (on): Very low resistance (R_ON, typically 0.1 ohm)
// - Open (off): Very high resistance (R_OFF, typically 10M ohm)
//
// This is a Single-Pole Single-Throw (SPST) switch.
// For SPDT (Single-Pole Double-Throw), use two Switch instances.
class Switch : public Component {
private:
bool closed;
double conductance;
static constexpr double R_ON = 0.1; // 0.1 ohm when closed
static constexpr double R_OFF = 50000000.0; // 50M ohm when open
void updateConductance();
public:
Switch(int n1, int n2, bool initialState = false);
// This component is dynamic (state changes with switch position)
bool isDynamic() const override;
double getConductance(double dt) const override;
double getCurrentSource(double dt) const override;
void updateState(double vA, double vB, double dt) override;
// Set switch state
void setClosed(bool state);
void setOpen(bool state);
void toggle();
bool isClosed() const;
bool isOpen() const;
};
// ==========================================
// Potentiometer Class
// ==========================================
// A potentiometer is modeled as two resistors in series:
// nodeA --- R_upper --- wiper (nodeW) --- R_lower --- nodeB
// The wiper position (0.0 to 1.0) determines the ratio
// When position = 0.0: R_upper = totalR, R_lower = 0 (wiper at nodeB)
// When position = 1.0: R_upper = 0, R_lower = totalR (wiper at nodeA)
//
// For audio taper (logarithmic), use setPositionLog()
class Potentiometer : public Component {
private:
int nodeWiper;
double totalResistance;
double position; // 0.0 to 1.0
double conductanceUpper;
double conductanceLower;
static constexpr double MIN_R = 1.0; // Minimum resistance to avoid division by zero
void updateConductances();
public:
// n1 = nodeA (top), n2 = nodeB (bottom), nW = wiper node
Potentiometer(int n1, int n2, int nW, double totalR, double initialPosition = 0.5);
// This component is dynamic (resistance changes with knob position)
bool isDynamic() const override;
// For a potentiometer, we need special handling - it's actually TWO resistors
// We'll return the combined parallel conductance for stamping purposes
// But the actual stamping needs to be done differently
double getConductance(double dt) const override;
double getCurrentSource(double dt) const override;
void updateState(double vA, double vB, double dt) override;
// Set wiper position (0.0 to 1.0, linear)
void setPosition(double pos);
// Set wiper position with audio/log taper
// Attempt to model a typical audio taper pot
void setPositionLog(double pos);
// Set wiper position with reverse log taper
void setPositionRevLog(double pos);
double getPosition() const;
int getWiperNode() const;
double getConductanceUpper() const;
double getConductanceLower() const;
double getTotalResistance() const;
};
// ==========================================
// 2. Optimized Circuit Engine
// ==========================================
template <std::size_t NumDevices, std::size_t NumNodes>
class Circuit {
private:
// Fixed Size Arrays for Optimization
std::array<std::shared_ptr<Component>, NumDevices> components{};
// Row-major flattened matrix for Y: Y[row][col] -> Y_static[row * NumNodes + col]
std::array<double, NumNodes * NumNodes> Y_static{};
std::array<double, NumNodes * NumNodes> Y_work{}; // Scratchpad for solver
std::array<double, NumNodes> J{}; // Current vector
std::array<double, NumNodes> nodes{}; // Solution (Voltages)
int currentDevices;
double dt;
// Internal Optimized Solver (Gaussian Elimination)
// Solves Y * x = J, result stored in J
void solveLinearSystem(int N);
public:
Circuit(double sampleRate);
void addComponent(std::shared_ptr<Component> c);
// ==========================================
// Preprocess: Builds the static Y Matrix
// Call this ONCE before audio starts
// ==========================================
void preprocess(double impedence);
// ==========================================
// Real-time Audio Process
// ==========================================
double process(double inputVoltage, int probeNode);
};
// ==========================================
// 3. Dynamic Circuit Engine (supports real-time parameter changes)
// ==========================================
template <std::size_t NumDevices, std::size_t NumNodes>
class DynamicCircuit {
private:
std::array<std::shared_ptr<Component>, NumDevices> components{};
std::array<std::shared_ptr<Potentiometer>, NumDevices> potentiometers{}; // Track pots separately
std::array<std::shared_ptr<Switch>, NumDevices> switches{}; // Track switches separately
std::array<double, NumNodes * NumNodes> Y_work{};
std::array<double, NumNodes> J{};
std::array<double, NumNodes> nodes{};
int currentDevices = 0;
int currentPots = 0;
int currentSwitches = 0;
double dt;
double sourceImpedance = 10.0;
void solveLinearSystem(int N);
void stampComponent(std::shared_ptr<Component>& comp);
void stampPotentiometer(std::shared_ptr<Potentiometer>& pot);
public:
DynamicCircuit(double sampleRate);
void addComponent(std::shared_ptr<Component> c);
void addPotentiometer(std::shared_ptr<Potentiometer> p);
void addSwitch(std::shared_ptr<Switch> s);
void setSourceImpedance(double impedance);
double process(double inputVoltage, int probeNode);
// Get a potentiometer by index for real-time adjustment
std::shared_ptr<Potentiometer> getPotentiometer(int index);
// Get a switch by index for real-time adjustment
std::shared_ptr<Switch> getSwitch(int index);
};
} // namespace MKAudio