Natural Frequency

\f\sub{0} = \frac{1}{2\pi} \tall{(}{170} \frac{s}{m} \tall{)}{170}\sup{1/2} = \reg{}{f0} [Hz]

Massm [ kg ]

Springs [ \ratio{N}{m} ]

Damperr [ \ratio{kg}{s} ]

Damping Rate

\beta = \tall{(}{130} \frac{r}{2m} \tall{)}{130} = \reg{}{dampRate} [\ratio{1}{s}]

Damped Frequency

\f\sub{d} = \frac{1}{2\pi} \tall{(}{170} \omega\sub{0}\sup{2} - \beta\sup{2} \tall{)}{170}\sup{1/2} = \reg{}{fd} [Hz]

Driving Force

F(t) = F\sub{A} cos\tall{(}{130} \omega t \tall{)}{130} F\sub{A} = \reg{}{force-amplitude} [N]

Displacement

\delta(t) = \delta\sub{A} cos\tall{(}{130} \omega t \tall{)}{130}

Amplitude

\delta\sub{A} = \frac{ F\sub{A} / m }{ \omega\sub{0}\sup{2} - \omega\sup{2} } \delta\sub{A} = \reg{}{m1-amp} [m]
let init = false color.add("mass","var(--theme-bright-light-2)","var(--theme-dull-dark-4)") color.add("mass-text","black","white") color.add("spring","var(--theme-bright-light-4)","var(--theme-dull-dark-3)") color.add("spring-text","white","black") color.add("damper","var(--theme-bright-light-4)","var(--theme-dull-dark-3)") color.add("damper-text","white","black") color.add("force","orange") color.add("mark","orange") const spaceWidth = this.width/4 const p1 = this.newPlot({ position: [ this.left+100, this.bottom+20 ], size: [spaceWidth, this.height-40], rangeX: [-3,3], rangeY: [-5,5], labelX: "", labelY: "Position x [m]", gridX: false, tickArrayX: [], tickEndsX: false, tickArrayX: [-10], tickLabelFontSizeY: 30, tickLabelGapY: 10, labelFontSizeY: 40, labelGapY: 15, }) const p2 = this.newPlot({ position: [ this.left + 100 + spaceWidth + 150, this.bottom + 2*this.height/5 + 50], size: [this.width - spaceWidth - 100 - 150 - 20 ,3*this.height/5 - 150 ], rangeX: [0,2], rangeY: [0,5], labelX: "", labelY: "Amplitude \\delta\\sub{A} [m]", tickStepX: 0.5, tickStepY: 1, labelFontSizeX: 40, labelFontSizeY: 40, }) const [p2x,p2y] = p2.position const [p2w,p2h] = p2.size p2.text({ text:"Displacement \\delta(t) = \\delta\\sub{A} cos(2\\pi\\f t - \\Phi)", position:[p2x+p2w/2,p2y+p2h+20], anchor:[0.5,0], screen: true, clip: false, fontSize: 40, }) const p3 = this.newPlot({ position: [ this.left + 100 + spaceWidth + 150, this.bottom + 100], size: [this.width - spaceWidth - 100 - 150 - 20 ,2*this.height/5 - 125 ], rangeX: [0,2], rangeY: [0,pi*1.01], labelX: "Driving Frequency \\f [Hz]", labelY: "Phase \\Phi [rad]", tickStepX: 0.5, tickArrayY: [0,{value:pi,label:"\\pi"},{value:pi/2,label:"\\pi/2"}], labelFontSizeX: 40, labelFontSizeY: 40, }) let ampM1, phaseM1, dispM1, fLength, f0, fd, dampRate const posM1 = 0 const posS1 = -5 const heightM = 1 const ampF = 50 reg.forceAmplitude.textContent = ampF reg.osc.onUpdate(t=>{ updatePosition() m1.update() s1.update() r1.update() force.update() }) reg.freq.onUpdate(()=>updateAll()) reg.m1.onUpdate(()=>updateAll()) reg.s1.onUpdate(()=>updateAll()) reg.r1.onUpdate(()=>updateAll()) reg.showForce.onUpdate(v=>{ force.classList.toggle("hidden",!v) reg.showForce.button.style.background = !v?"":color.force }) function updateAll(){ updateResponse() updatePosition() updateGroups() } function updateGroups(){ m1.update() s1.update() r1.update() force.update() frf.update() driveMark.update() } function updateResponse(){ const m = reg.m1.value const s = reg.s1.value const r = reg.r1.value const f = reg.freq.value f0 = sqrt(s/m) / pi2 reg.f0.textContent = format(f0,2) dampRate = r/2/m if(dampRate**2 > (f0*pi2)**2){ reg.r1.value = 2 * m * f0 * pi2 return } reg.dampRate.textContent = format(dampRate,2) fd = sqrt( (pi2*f0)**2 - (dampRate)**2 )/pi2 reg.fd.textContent = format(fd,2) ampM1 = amplitude(f,m,s,r) phaseM1 = phase(f,m,s,r) reg.m1Amp.textContent = format(ampM1,2) if(init) s1Spr.style.strokeWidth = 1 + 6*reg.s1.ratio } updateResponse() function updatePosition(){ const T = pi2*reg.freq.value*reg.osc.value fLength = cos(T) dispM1 = ampM1*cos(T - phaseM1) } updatePosition() function amplitude(f,m,s,r){ const w = pi2*f const w0 = sqrt(s/m) const B = r/2/m return ( ampF / m ) / hypot(w0**2 - w**2, 2*B*w) } function phase(f,m,s,r){ const w = pi2*f const w0 = sqrt(s/m) const B = r/2/m return atan(2*B*w/(w0**2 - w**2)) } const m1 = p1.group("m1") const m1Rect = p1.rect({ position: ()=>[0,posM1 + dispM1], size: ()=>[2.1 + 2*reg.m1.value/reg.m1.max,heightM], fill: color.mass, corner: 10, group: m1, }) p1.text({ position: ()=>[0,posM1 + dispM1], text: "m", group: m1, color: color.massText }) const force = p1.arrow({ start:()=>[2,posM1 + heightM/2 + dispM1], end:()=>[2,posM1 + heightM/2 + dispM1 + fLength], size:()=>1/2, fill:color.force, stroke:color.force, strokeWidth:5 }) reg.showForce.value = true const s1 = p1.group("s1") const s1Spr = p1.spring({ start:()=>[-1,posS1], end:()=>[-1,posM1 - heightM/2 + dispM1], coils:8, diameter:1.5, group:s1, stroke:color.spring, }) p1.rect({ position: ()=>[-2.8,(posS1 + posM1 + dispM1)/2], size: ()=>[p1.fromXsize(50),p1.fromYsize(50)], anchor: [0,0.5], fill: "red", corner: 10, group: s1, fill: color.mass, opacity:0.25 }) p1.text({ position: ()=>[-2.8 + p1.fromXsize(50)/2,(-5 + posM1 + dispM1)/2], text: "s", group: s1, color: color.springText }) const r1 = p1.group("r1") p1.damper({ start:()=>[1,posS1], end:()=>[1,posM1 - heightM/2 + dispM1], gap:()=>0.25*(1-reg.r1.ratio)+0.1, atRatio:()=>dispM1/5, diameter:1.5, group:r1, stroke:color.damper, }) p1.rect({ position: ()=>[2.8,(posS1 + posM1)*(3/4)], size: ()=>[p1.fromXsize(50),p1.fromYsize(50)], anchor:[1,0.5], fill: "red", corner: 10, group: r1, fill: color.mass, opacity:0.25 }) p1.text({ position: ()=>[2.8-p1.fromXsize(50)/2,(-5 + posM1)*(3/4)], text: "r", group: r1, color: color.springText }) const frf = p3.group("frf") p2.plotFn({ y: f => amplitude(f,reg.m1.value,reg.s1.value,reg.r1.value), group: frf, }) p3.plotFn({ y: f => { const Ph = phase(f,reg.m1.value,reg.s1.value,reg.r1.value) return Ph>=0?Ph:Ph+pi }, group: frf }) p2.line({ x: ()=>[f0,f0], y: ()=>[p2.fromY(p3.toY(p3.y.min)),p2.y.max], group: frf, strokeWidth: 1, clip: false, dash: "20 5" }) p2.text({ clip: false, text: "\\f\\sub{0}", color: color.defaultLine, background: color.plotBackground, group: frf, position: ()=>[f0,4.5] }) const driveMark = p3.group("mark") p2.line({ x: ()=>[reg.freq.value,reg.freq.value], y: ()=>[p2.fromY(p3.toY(p3.y.min)),p2.y.max], stroke: color.mark, group: driveMark, strokeWidth: 1, clip: false, }) p2.circle({ position: ()=>[reg.freq.value,ampM1], radius: 6, fill: color.mark, stroke: color.mark, group: driveMark, }) p3.circle({ position: ()=>[reg.freq.value,phaseM1>=0?phaseM1:phaseM1+pi], radius: 6, fill: color.mark, stroke: color.mark, group: driveMark, }) init = true