{"id":193,"date":"2025-09-03T14:19:12","date_gmt":"2025-09-03T06:19:12","guid":{"rendered":"https:\/\/bluespider.top\/?p=193"},"modified":"2025-09-03T14:19:12","modified_gmt":"2025-09-03T06:19:12","slug":"fft%ef%bc%8c%e5%a6%82%e6%9e%9c%e6%b2%a1%e6%9c%89simulink%ef%bc%8cpowergui%e5%b0%b1%e4%b8%8d%e4%bc%9a%e4%ba%86%ef%bc%9f%ef%bc%9f","status":"publish","type":"post","link":"http:\/\/bluespider.top\/?p=193","title":{"rendered":"FFT\uff0c\u5982\u679c\u6ca1\u6709simulink\uff0cpowergui\u5c31\u4e0d\u4f1a\u4e86\uff1f\uff1f"},"content":{"rendered":"<p>A:<br \/>\nI have a theta_a_lst with size 1&#215;721, It also represents one cycle of 50Hz sin wave. I also have voltage waveform va_lst. Perform FFT on va_lst.<\/p>\n<p>Q:<br \/>\nGot it. Since you have one cycle of a 50 Hz sine (theta_a_lst size 1&#215;721) and a corresponding voltage waveform va_lst, here are concise MATLAB and Python options to compute the FFT, report amplitudes\/phases, and plot up to Nyquist.<\/p>\n<p>Assumptions<br \/>\n&#8211; theta_a_lst spans exactly one electrical cycle (0 to 2\u03c0 or 0 to 360\u00b0) with 721 samples.<br \/>\n&#8211; va_lst is the same length as theta_a_lst.<br \/>\n&#8211; Effective sampling frequency Fs is derived from 50 Hz fundamental: Fs = 50 \u00d7 N (samples per cycle).<br \/>\n&#8211; Frequency resolution df = Fs\/N = 50 Hz (harmonic bins at 0, 50, 100, &#8230;).<\/p>\n<p>\u8fd9\u91cc\u7684\u6709\u6548\u91c7\u6837\u9891\u7387\uff08effective sampling frequency\uff09Fs\u5f88\u91cd\u8981\uff0ctheta_lst\u4e3a\u4e00\u4e2a50Hz\u57fa\u6ce2\u5468\u671f\uff0c\u5176\u4e2d\u70b9\u6570N\u8d8a\u5927\uff0cFs\u8d8a\u5927\uff0c\u4f46\u662f\u9891\u7387\u5206\u8fa8\u7387df\u5374\u662f\u4e0d\u53d8\u7684\uff0c\u56e0\u4e3aFs = 50xN, \u800cdf = Fs\/N, \u6240\u4ee5df = 50Hz\u3002<\/p>\n<p>\u5982\u679c\u53ea\u7ed9\u4e00\u4e2a50Hz\u7684\u57fa\u6ce2\u6240\u5bf9\u5e94\u7684\u5e8f\u5217\uff0c\u80fd\u5206\u6790\u51fa\u6765\u7684\u8c10\u6ce2\u80af\u5b9a\u90fd\u662f50Hz\u7684\u500d\u6570\u3002<\/p>\n<p>MATLAB version<\/p>\n<pre><code class=\"language-matlab line-numbers\">function out = fft_va_from_theta(theta_a_lst, va_lst, varargin)\n% fft_va_from_theta  FFT of va given one-cycle theta vector (50 Hz fundamental)\n% Usage:\n%   out = fft_va_from_theta(theta_a_lst, va_lst, 'FmaxPlot', 2500, 'DoPlot', true)\n%\n% Inputs:\n%   theta_a_lst : 1xN or Nx1 angles covering one cycle (rad or deg)\n%   va_lst      : 1xN or Nx1 voltage samples corresponding to theta_a_lst\n%\n% Name-Value:\n%   'F0'        : fundamental frequency (default 50 Hz)\n%   'Window'    : window name or vector (default 'hann')\n%   'DoPlot'    : true\/false (default true)\n%   'FmaxPlot'  : max frequency for plots (default min(2500, Fs\/2))\n%\n% Output struct out:\n%   .Fs, .df, .N, .f1 (kmaxx1), .X1 (kmaxx1 complex)\n%   .A1 (single-sided amplitude), .Sxx1 (single-sided PSD, arbitrary units if no Ts)\n%   .DC, .peak_amp, .peak_freq\n%   .idx_harm (indices at integer harmonics), .A1_harm, .phase_harm\n\n    p = inputParser;\n    p.addParameter('F0', 50, @(x)isnumeric(x)&amp;&amp;isscalar(x)&amp;&amp;x&gt;0);\n    p.addParameter('Window','hann');\n    p.addParameter('DoPlot', true, @(x)islogical(x)&amp;&amp;isscalar(x));\n    p.addParameter('FmaxPlot', 2500, @(x)isnumeric(x)&amp;&amp;isscalar(x)&amp;&amp;x&gt;0);\n    p.parse(varargin{:});\n    F0 = p.Results.F0;\n    wopt = p.Results.Window;\n    DoPlot = p.Results.DoPlot;\n\n    % Ensure column vectors\n    theta = theta_a_lst(:);\n    v = va_lst(:);\n    N = numel(v);\n    assert(numel(theta)==N, 'theta_a_lst and va_lst must have same length');\n\n    % Sampling frequency from one-cycle assumption\n    Fs = F0 * N;\n    df = Fs \/ N;   % equals F0\n\n    % Window\n    if ischar(wopt) || (isstring(wopt) &amp;&amp; isscalar(wopt))\n        switch lower(string(wopt))\n            case \"hann\"\n                w = hann(N, 'periodic');\n            case \"hamming\"\n                w = hamming(N, 'periodic');\n            case \"rect\"\n                w = ones(N,1);\n            otherwise\n                error('Unsupported Window option.');\n        end\n    else\n        w = wopt(:);\n        assert(numel(w)==N, 'Custom window must match data length');\n    end\n    W0 = sum(w)\/N;        % coherent gain\n    Pw = mean(w.^2);      % window power (for PSD if needed)\n    vw = v .* w;\n\n    % FFT\n    X = fft(vw);\n    Nfft = N;\n    f = (0:Nfft-1).' * (Fs\/Nfft);\n\n    % Single-sided\n    if mod(Nfft,2)==0\n        kmax = Nfft\/2 + 1;\n    else\n        kmax = (Nfft+1)\/2;\n    end\n    f1 = f(1:kmax);\n    X1 = X(1:kmax);\n\n    % Amplitude spectrum (single-sided), window-corrected\n    A2 = abs(X1) \/ (N * W0);\n    A1 = A2;\n    if kmax &gt; 2\n        A1(2:end-1) = 2*A1(2:end-1);\n    end\n\n    % PSD (single-sided). Units V^2\/Hz if Ts known; here Ts=1\/Fs.\n    Sxx2 = (abs(X1).^2) \/ (Fs * N * Pw);\n    Sxx1 = Sxx2;\n    if kmax &gt; 2\n        Sxx1(2:end-1) = 2*Sxx1(2:end-1);\n    end\n\n    % Stats\n    DC = A1(1);\n    [peak_amp, idxp] = max(A1(2:end)); idxp = idxp + 1;\n    peak_freq = f1(idxp);\n\n    % Harmonic bins (0, F0, 2F0, ...)\n    harmonics = (0:floor(f1(end)\/F0)).' * F0;\n    [~, idx_harm] = ismember(harmonics, f1); % exact because df=F0\n    A1_harm = A1(idx_harm);\n    phase_harm = angle(X1(idx_harm));\n\n    % Pack outputs\n    out = struct('Fs',Fs,'df',df,'N',N,'f1',f1,'X1',X1, ...\n                 'A1',A1,'Sxx1',Sxx1,'DC',DC, ...\n                 'peak_amp',peak_amp,'peak_freq',peak_freq, ...\n                 'idx_harm',idx_harm,'A1_harm',A1_harm,'phase_harm',phase_harm, ...\n                 'W0',W0,'Pw',Pw);\n\n    % Plots\n    if DoPlot\n        fmax_plot = min(2500, Fs\/2);\n        idx_plot = f1 &lt;= fmax_plot;\n\n        fig = figure('Name','va FFT (one-cycle, 50 Hz)','Color','w');\n        % scale 50%\n        pos = get(fig,'Position'); pos(3:4) = 0.5*pos(3:4); set(fig,'Position',pos);\n\n        tiledlayout(3,1);\n        nexttile;\n        plot((0:N-1)\/Fs, v, 'k-'); grid on;\n        xlabel('Time (s)'); ylabel('v_a');\n        title('Time-domain (one cycle)');\n\n        nexttile;\n        stem(f1(idx_plot), A1(idx_plot), 'filled'); grid on;\n        xlim([0, fmax_plot]); xlabel('Frequency (Hz)'); ylabel('Amplitude (V)');\n        title(sprintf('Single-Sided Amplitude Spectrum (df = %.1f Hz)', df));\n\n        nexttile;\n        plot(f1(idx_plot), 10*log10(Sxx1(idx_plot)+eps), 'b-'); grid on;\n        xlim([0, fmax_plot]); xlabel('Frequency (Hz)'); ylabel('PSD (dBV^2\/Hz)');\n        title('Single-Sided PSD');\n    end\nend\n<\/code><\/pre>\n<p>Python version (NumPy)<\/p>\n<pre><code class=\"language-python line-numbers\">import numpy as np\n\ndef fft_va_from_theta(theta_a_lst, va_lst, f0=50.0, window='hann'):\n    \"\"\"\n    FFT of va given one-cycle theta vector (50 Hz fundamental).\n    Returns dict with Fs, df, f1, X1, A1, phases, etc.\n    \"\"\"\n    theta = np.asarray(theta_a_lst).ravel()\n    v = np.asarray(va_lst).ravel()\n    assert theta.size == v.size\n    N = v.size\n    Fs = f0 * N\n    df = Fs \/ N  # equals f0\n\n    if isinstance(window, str):\n        if window.lower() == 'hann':\n            w = np.hanning(N)\n        elif window.lower() == 'hamming':\n            w = np.hamming(N)\n        elif window.lower() == 'rect':\n            w = np.ones(N)\n        else:\n            raise ValueError('Unsupported window')\n    else:\n        w = np.asarray(window).ravel()\n        assert w.size == N\n    W0 = w.sum()\/N\n    Pw = np.mean(w**2)\n\n    vw = v * w\n    X = np.fft.fft(vw)\n    Nfft = N\n    f = np.arange(Nfft) * (Fs\/Nfft)\n\n    kmax = Nfft\/\/2 + 1 if Nfft % 2 == 0 else (Nfft+1)\/\/2\n    f1 = f[:kmax]\n    X1 = X[:kmax]\n\n    A2 = np.abs(X1) \/ (N * W0)\n    A1 = A2.copy()\n    if kmax &gt; 2:\n        A1[1:-1] *= 2.0\n\n    Sxx2 = (np.abs(X1)**2) \/ (Fs * N * Pw)\n    Sxx1 = Sxx2.copy()\n    if kmax &gt; 2:\n        Sxx1[1:-1] *= 2.0\n\n    DC = A1[0]\n    idxp = 1 + np.argmax(A1[1:])\n    peak_amp = A1[idxp]\n    peak_freq = f1[idxp]\n\n    harmonics = np.arange(int(f1[-1]\/\/f0)+1) * f0\n    idx_harm = (harmonics \/ df).astype(int)\n    A1_harm = A1[idx_harm]\n    phase_harm = np.angle(X1[idx_harm])\n\n    return dict(Fs=Fs, df=df, N=N, f1=f1, X1=X1, A1=A1, Sxx1=Sxx1, DC=DC,\n                peak_amp=peak_amp, peak_freq=peak_freq, idx_harm=idx_harm,\n                A1_harm=A1_harm, phase_harm=phase_harm, W0=W0, Pw=Pw)\n<\/code><\/pre>\n<p>If you share the units\/format of theta_a_lst (deg vs rad) or want a specific window, I can tailor the code further.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A: I have a theta_a_lst with size 1&#215;721, It also r [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[18],"tags":[],"class_list":["post-193","post","type-post","status-publish","format-standard","hentry","category-undergraduate_competition"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"http:\/\/bluespider.top\/index.php?rest_route=\/wp\/v2\/posts\/193","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/bluespider.top\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/bluespider.top\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/bluespider.top\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/bluespider.top\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=193"}],"version-history":[{"count":1,"href":"http:\/\/bluespider.top\/index.php?rest_route=\/wp\/v2\/posts\/193\/revisions"}],"predecessor-version":[{"id":194,"href":"http:\/\/bluespider.top\/index.php?rest_route=\/wp\/v2\/posts\/193\/revisions\/194"}],"wp:attachment":[{"href":"http:\/\/bluespider.top\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=193"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/bluespider.top\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=193"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/bluespider.top\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=193"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}