; Realtime rendering using default DAC with 48kHz -o dac -d --messagelevel=2 After 6.17 GNU General Public Licence - Version 3 ; ============================================== sr = 48000 ksmps = 1 nchnls = 2 0dbfs = 1 ; SVM modelled as closely as possible using mpulse and trighold for PWM instr Pwm2 ; Basic setup ; kFac drives the whole thing from 0 to 1, where 1 = full power kFac = linseg:k(.01, p3/5, .05, p3/5, 1/5, p3/5, 1/5, p3/5, 1, p3/5, 1) kFreq = linlin:k(kFac, 0, 50) ; Motor frequency kTs = 1/5000 ; switching time of the inverter iTs init 1/5000 ; Initial switching time, used for iOffset in mpulse kFreqS = 1 / kTs ; Switching frequency kDCVolt = linlin:k(kFac, 0, .4) ; DC volt, i.e. how much "power" do we give ; between 0 and 1 kT1 init 0 ; first pulsewidth time kT2 init 0 ; second pulsewidth time aTrig[] init 3 ; pulse trains, the on/off signals from the inverter kTrig = metro(kFreqS) ; whenever this is 1, we calculate new values aPhase[] init 3 ; Inverter phase outputs, what we will hear kAmp[] init 3 ; Amplitudes of the switching pulses, basically the real ; on/off state kHold[] init 3 ; pulsewidth/hold times for the three pulse train switches ; Create v_a and v_b sine inputs that we need for the SVM and a few utility ; signals ;aVa is a sine at 0-phase i.e. aSin below aVb = oscil:a(1, kFreq, -1, 1/3) aSin = oscil:a(1, kFreq, -1, 0) aCos = oscil:a(1, kFreq, -1, .5) ; Do the alpha-beta transformation to get the D and Q signals aAlpha = aSin aBeta = sqrt(1/3) * (aSin + 2 * aVb) aVd = aAlpha * aCos + aBeta * aSin aVq = -aAlpha * aSin + aBeta * aCos ; Derive the reference signal from D and Q aRef = sqrt(aVd^2 + aVq^2) aThetaRef = taninv2:a(aVq, aVd) kAlpha = wrap:k(k(aThetaRef), 0, 1/6) ; Calculate the pulse times/periods T1 and T2 kT1 = kTs * k(aRef)/kDCVolt * sin($M_PI * 2/3 - kAlpha) kT2 = kTs * k(aRef)/kDCVolt * sin(kAlpha) ; Convert the phase/angle of the reference signal to 0-1 range to help ; with the "sectors" and get the required alpha angle kTmp = k(aThetaRef) / (2 * $M_PI) + .5 kTR = wrap:k((kTmp + 0/6), 0, 1) ; Sector check and real pulsewidth and on/off calculation for the ; three pulse signals, this follows a preset sequence. if kTrig != 0 then if (kTR >=0) && (kTR <1/6) then kHold[0] = kT1 + kT2 kHold[1] = kT2 kAmp[0] = 1 kAmp[1] = 1 kAmp[2] = 0 elseif (kTR >= 1/6) && (kTR <2/6) then kHold[0] = kT1 kHold[1] = kT1 + kT2 kAmp[0] = 1 kAmp[1] = 1 kAmp[2] = 0 elseif (kTR >= 2/6) && (kTR <3/6) then kHold[1] = kT1 + kT2 kHold[2] = kT2 kAmp[0] = 0 kAmp[1] = 1 kAmp[2] = 1 elseif (kTR >= 3/6) && (kTR <4/6) then kHold[1] = kT1 kHold[2] = kT1 + kT2 kAmp[0] = 0 kAmp[1] = 1 kAmp[2] = 1 elseif (kTR >= 4/6) && (kTR <5/6) then kHold[0] = kT2 kHold[2] = kT1 + kT2 kAmp[0] = 1 kAmp[1] = 0 kAmp[2] = 1 elseif (kTR >= 5/6) && (kTR <1) then kHold[0] = kT1 + kT2 kHold[2] = kT1 kAmp[0] = 1 kAmp[1] = 0 kAmp[2] = 1 endif endif ; Run the pulse trains and derive PWM signals aTrig[0] = mpulse(1, kTs, 0) aTrig[1] = mpulse(1, kTs, i(kTs) * 1/3) aTrig[2] = mpulse(1, kTs, i(kTs) * 2/3) aPhase[0] = trighold:a(aTrig[0], kHold[0]) * kAmp[0] aPhase[1] = trighold:a(aTrig[1], kHold[1]) * kAmp[1] aPhase[2] = trighold:a(aTrig[2], kHold[2]) * kAmp[2] ; Output and filter the audio iLpf init 1/iTs aOutL = dcblock(butterlp((aPhase[0]*.5 + aPhase[1]*.25), iLpf)) aOutR = dcblock(butterlp((aPhase[2]*.5 + aPhase[1]*.25), iLpf)) outs(aOutL, aOutR) endin ; Optimised SVM ; Simplified SVM using vco2 instr Pwm3 ; Basic control parameters kFreqFac = linseg:k(0, p3/4, 1/5, p3/4, 3/5, p3/4, 1, p3/4, 1) kEngineFreq = linlin:k(kFreqFac, 0, 50) kSwitchFreq = 5000 kDCVolt = linlin:k(kFreqFac, 0, .6) ; Initialise more variables for the process kT1 init 0 ; T1 on-time kT2 init 0 ; T2 on-time kSwitchTrig = metro(kSwitchFreq) ; Trigger to evaluate new pulsewidth ; Internal utility signals ; Basic sine waves with different phases aSin = oscil:a(1, kEngineFreq, -1, 0) aSin120 = oscil:a(1, kEngineFreq, -1, 1/3) aCos = oscil:a(1, kEngineFreq, -1, .5) ; The alpha-beta transform v_alpha is aSin aBeta = sqrt(1/3) * (aSin + 2 * aSin120) ; Derive DQ signals aV_d = (aSin * aCos) + (aBeta * aSin) aV_q = (aBeta * aCos) - (aSin^2) ; Derive reference signal aRefSig = sqrt(aV_d^2 + aV_q^2) kRefTheta = k(taninv2:a(aV_q, aV_d)) kAlpha = wrap:k(kRefTheta, 0, 1/6) ; Angle between reference signal and ; first vector in this sector, secors cover 1/6th of the circle ; Initialise phase outputs, these will carry the PWM signals aPhase[] init 3 kPW[] init 3 ; pulsewidth controls ; Rescale the angle of the reference signal to 0-1 kMyTheta = linlin:k(kRefTheta, 0, 1, -$M_PI, $M_PI) ; Set up the next switching cycle if (kSwitchTrig != 0) then ; Calculate switching-times normalised to 0-1 range ; the SVM formula will multiple them by the switching time, i.e. ; length of one cycle kT1 = k(aRefSig) / kDCVolt * sin($M_PI/3 - kAlpha) kT2 = k(aRefSig) / kDCVolt * sin(kAlpha) ; Now set actual pulsewidth and on/off states if (kMyTheta >0) && (kMyTheta <= 1/6) then kPW[0] = kT1 + kT2 kPW[1] = kT2 kPW[2] = 0 elseif (kMyTheta >1/6) && (kMyTheta <= 2/6) then kPW[0] = kT1 kPW[1] = kT1 + kT2 kPW[2] = 0 elseif (kMyTheta >2/6) && (kMyTheta <= 3/6) then kPW[0] = 0 kPW[1] = kT1 + kT2 kPW[2] = kT2 elseif (kMyTheta >3/6) && (kMyTheta <= 4/6) then kPW[0] = 0 kPW[1] = kT1 kPW[2] = kT1 + kT2 elseif (kMyTheta >4/6) && (kMyTheta <= 5/6) then kPW[0] = kT2 kPW[1] = 0 kPW[2] = kT1 + kT2 else kPW[0] = kT1 + kT2 kPW[1] = 0 kPW[2] = kT1 endif endif ; Create the phase outputs kPF = linlin:k(kFreqFac, 1, 1.5) kPW = limit(kPW, .01, .99) / kPF aPhase[0] = vco2(.5, kSwitchFreq, 2, kPW[0], 0) aPhase[1] = vco2(.5, kSwitchFreq, 2, kPW[1], 1/3) aPhase[2] = vco2(.5, kSwitchFreq, 2, kPW[2], 2/3) ; Output and filter the phases aOutL = butterlp(dcblock(aPhase[0]*.5 + aPhase[1]*.25), kSwitchFreq) aOutR = butterlp(dcblock(aPhase[2]*.57 + aPhase[1]*.25), kSwitchFreq) outs(aOutL, aOutR) endin ; ============================================== i"Pwm2" 0 12 ; closely modelled SVM i"Pwm3" 13 12 ; simplified SVM with vco2 e Csound mailing list Csound@listserv.heanet.ie https://listserv.heanet.ie/cgi-bin/wa?A0=CSOUND Send bugs reports to https://github.com/csound/csound/issues Discussions of bugs and features can be posted here