// Automatically generated C++ file on Mon Jul 13 2026 #include // M_PI definitie voor het geval de compiler deze niet standaard activeert #ifndef M_PI #define M_PI 3.14159265358979323846 #endif union uData { bool b; char c; unsigned char uc; short s; unsigned short us; int i; unsigned int ui; float f; double d; long long int i64; unsigned long long int ui64; char *str; unsigned char *bytes; }; // Structuur om de status van de PLL tussen stappen te onthouden struct PllState { double v_alpha; double v_beta; double q_integrator; double theta_internal; bool prev_clk; }; int __stdcall DllMain(void *module, unsigned int reason, void *reserved) { return 1; } #undef Vac_in #undef CLK #undef theta #undef sin_theta #undef cos_theta extern "C" __declspec(dllexport) void pll_srf(void **opaque, double t, union uData *data) { double Vac_in = data[0].d; // input (0V - 3.3V, offset 1.65V) bool CLK = data[1].b; // input double &theta = data[2].d; // output double &sin_theta = data[3].d; // output double &cos_theta = data[4].d; // output // 1. Initialiseer persistent geheugen (opaque) bij de allereerste stap if (*opaque == nullptr) { PllState *state = new PllState(); state->v_alpha = 0.0; state->v_beta = 0.0; state->q_integrator = 0.0; state->theta_internal = 0.0; state->prev_clk = false; *opaque = state; } // Cast het opaque geheugen terug naar onze bruikbare struct PllState *s = (PllState*)*opaque; // 2. Vaste parameters voor de 100 kHz klok const double dt = 10e-6; const double w_grid = 2.0 * M_PI * 50.0; // 3. Detecteer de stijgende flank van de CLK input (flankdetectie) if (CLK && !s->prev_clk) { // Neem de binnengekomen spanning en haal de 1.65V offset eraf double v_ac_centered = Vac_in - 1.65; // SOGI (Orthogonal Signal Generator) met symmetrische integratie double k_sogi = 1.414; double error_sogi = v_ac_centered - s->v_alpha; // Onthoud de oude v_alpha van de vorige stap voor de v_beta integrator double v_alpha_old = s->v_alpha; // Update beide integrators onafhankelijk op basis van de status van de vorige stap s->v_alpha += (w_grid * k_sogi * error_sogi - w_grid * w_grid * s->v_beta) * dt; s->v_beta += (w_grid * v_alpha_old) * dt; // Industriestandaard Park Transformatie (Lock op stijgende nuldoorgang) double v_d = s->v_alpha * cos(s->theta_internal) + s->v_beta * sin(s->theta_internal); double v_q = -s->v_alpha * sin(s->theta_internal) + s->v_beta * cos(s->theta_internal); // AMPLITUDE-NORMALISATIE MET NOMINALE REFERENTIE double v_amplitude = sqrt(s->v_alpha * s->v_alpha + s->v_beta * s->v_beta); // Voorkom delen door nul bij het opstarten, val terug op nominale waarde if (v_amplitude < 0.1) { v_amplitude = 1.65; } // Schaal de fout terug naar het nominale niveau (1.65V) om de loop gain constant te houden double error_pll = (0.0 - v_q) * (1.65 / v_amplitude); s->q_integrator += error_pll * dt; // Anti-windup if (s->q_integrator > 50.0) s->q_integrator = 50.0; if (s->q_integrator < -50.0) s->q_integrator = -50.0; // Stabiele basis-tuning (afgestemd op de nominale versterking) double kp_actual = 40.0; double ki_actual = 2000.0; double w_estimated = w_grid + (kp_actual * error_pll) + (ki_actual * s->q_integrator); // Integreren naar fasehoek theta s->theta_internal += w_estimated * dt; // Hoek netjes binnen 0 tot 2*pi houden if (s->theta_internal > 2.0 * M_PI) s->theta_internal -= 2.0 * M_PI; if (s->theta_internal < 0.0) s->theta_internal += 2.0 * M_PI; } // Update de klokstatus voor de volgende stap s->prev_clk = CLK; // 4. Koppel de berekende interne waarden continu terug naar de Qspice outputs theta = s->theta_internal; sin_theta = sin(s->theta_internal); cos_theta = cos(s->theta_internal); }