Resonant
Frequencies
\f\sub{0} = \reg{}{f0} [Hz]
\f\sub{1} = \reg{}{f1} [Hz]
Massm\sub{1} [ kg ]
Springs\sub{1} [ \ratio{N}{m} ]
Damperr\sub{1} [ \ratio{kg}{s} ]
\beta\sub{1} = \reg{}{B1} [1/s]
Anti-Resonant
Frequency
\f\sub{-1} = \tall{(}{130} \frac{s\sub{2}}{m\sub{2}} \tall{)}{130}\sup{1/2}
= \reg{}{f_1} [Hz]
Massm\sub{2} [ kg ]
Springs\sub{2} [ \ratio{N}{m} ]
Damperr\sub{2} [ \ratio{kg}{s} ]
\beta\sub{2} = \reg{}{B2} [1/s]
Displacement
Amplitudes
\delta\sub{A\sub{1}} = \reg{}{m1-amp} [m]
\delta\sub{A\sub{2}} = \reg{}{m2-amp} [m]
Displacement
Phases
\Phi\sub{1} = \reg{}{m1-phase} \pi [rad]
\Phi\sub{2} = \reg{}{m2-phase} \pi [rad]
Plot phase relative to
Driving Force
F(t) = F\sub{A} cos\tall{(}{130} \omega t \tall{)}{130}
F\sub{A} = \reg{}{force-amplitude} [N]
let init = false
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,10],
labelX: "",
labelY: "Position x [m]",
gridX: false,
tickEndsX: false,
tickArrayX: [-10],
tickLabelFontSizeY: 30,
tickLabelGapY: 10,
tickStepY: 1,
labelFontSizeY: 40,
labelGapY: 15,
})
const p2 = this.newPlot({
position: [ this.left + 100 + spaceWidth + 150, this.center-50],
size: [this.width - spaceWidth - 100 - 150 - 20 ,this.height/2 - 40 ],
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: [p2x,this.bottom+100],
size: [p2w,p2h - 170],
rangeX: [0,2],
rangeY: [0,pi2*1.00001],
labelX: "Driving Frequency \\f [Hz]",
labelY: "Phase \\reg{\\Phi}{phaseTypeLab} [rad]",
tickStepX: 0.5,
tickArrayY: [0,{value:pi,label:"\\pi"},{value:pi/2,label:"\\frac{\\pi}{2}"},{value:3*pi/2,label:"3\\frac{\\pi}{2}"},{value:pi2,label:"2\\pi"}],
labelFontSizeX: 40,
labelFontSizeY: 40,
})
let ampM1, phaseM1, dispM1, ampM2, phaseM2, dispM2, fLength, f0, f1, dampRatio1, dampRatio2
const posM1 = 0
const posS1 = -5
const posM2 = 5
const heightM = 1
const posS2 = posM1 + heightM
const ampF = 50
reg.forceAmplitude.textContent = ampF
reg.osc.onUpdate(t=>{
updatePosition()
m1g.update()
s1g.update()
r1g.update()
m2g.update()
s2g.update()
r2g.update()
force.update()
})
reg.freq.onUpdate(()=>updateAll())
reg.m1.onUpdate(()=>updateAll())
reg.s1.onUpdate(()=>updateAll())
reg.r1.onUpdate(()=>updateAll())
reg.m2.onUpdate(()=>updateAll())
reg.s2.onUpdate(()=>updateAll())
reg.r2.onUpdate(()=>updateAll())
reg.showForce.onUpdate(v=>{
force.classList.toggle("hidden",!v)
reg.showForce.button.style.background = !v?"":color.highlight
reg.showForce.button.style.color = !v?"white":"black"
})
reg.phaseType.onUpdate(v=>{
reg.phaseType.button.style.background = v?"":color.highlight
reg.phaseType.button.style.color = v?"white":"black"
frfPh1.classList.toggle("hidden",v)
frfPh2.classList.toggle("hidden",v)
frfPhDiff.classList.toggle("hidden",!v)
reg.phaseTypeLab.update(!v?"\\Phi":"\\Phi\\sub{2} - \\Phi\\sub{1}")
updateGroups()
})
function updateAll(){
updateResponse()
updatePosition()
updateGroups()
}
function updateGroups(){
m1g.update()
s1g.update()
r1g.update()
m2g.update()
s2g.update()
r2g.update()
force.update()
frf.update()
frfPh.update()
freqMarks.update()
}
function updateResponse(){
const m1 = reg.m1.value
const s1 = reg.s1.value
const r1 = reg.r1.value
const m2 = reg.m2.value
const s2 = reg.s2.value
const r2 = reg.r2.value
const f = reg.freq.value
const fT = frequency(m1,s1,r1,m2,s2,r2)
f0 = fT.res[0]/pi2
f1 = fT.res[1]/pi2
f_1 = fT.anti[0]/pi2
reg.f0.textContent = format(f0,2)
reg.f1.textContent = format(f1,2)
reg.f_1.textContent = format(f_1,2)
reg.B1.textContent = format(r1/2/m1,2)
reg.B2.textContent = format(r1/2/m1,2)
const [A1,A2,ph1,ph2] = ampPhase(f,m1,s1,r1,m2,s2,r2)
ampM1 = A1
ampM2 = A2
reg.m1Amp.textContent = format(ampM1,2)
reg.m2Amp.textContent = format(ampM2,2)
phaseM1 = ph1
phaseM2 = ph2
reg.m1Phase.textContent = format(phaseM1/pi,2)
reg.m2Phase.textContent = format(phaseM2/pi,2)
if(init){
s1Spr.style.strokeWidth = 1 + 4*reg.s1.ratio
s2Spr.style.strokeWidth = 1 + 4*reg.s2.ratio
}
}
function updatePosition(){
const T = pi2*reg.freq.value*reg.osc.value
fLength = cos(T)
dispM1 = ampM1*cos(T - phaseM1)
dispM2 = ampM2*cos(T - phaseM2)
}
updateResponse()
updatePosition()
function frequency(m1, s1, r1, m2, s2, r2){
const A = (s1 + s2)*m2 + m1*s2
const B = sqrt(A**2 - 4*m1*m2*s1*s2)
const C = 2*m1*m2
const res = [sqrt((A - B) / C), sqrt((A + B) / C)]
const anti = [sqrt(s2/m2)]
return {res,anti}
}
function ampPhase(f, m1, s1, r1, m2, s2, r2){
const w = pi2*f
const w1 = sqrt(s1/m1)
const w2 = sqrt(s2/m2)
const B1 = r1/(2*m1)
const B2 = r2/(2*m2)
const a = w**4 - ( w1**2 + w2**2*( 1 + m2/m1 ) + 4*B1*B2 )*w**2 + w1**2 * w2**2
const b = ( B1*w1*w2**2 + B2*w1**2 )*w - ( B1 + B2*( 1 + m2/m1 ) )*w**3
const A1 = (ampF / s1) * w1**2 * hypot(w2**2 - w**2, 2*B2*w) / hypot(a, 2*b)
const A2 = (ampF / s2) * w1**2 * hypot(w2**2, 2*B2*w) / hypot(a, 2*b)
const phD = atan(2*b, a)
const ph1 = phD - atan(2*B2*w, w2**2 - w**2)
const ph2 = phD - atan(2*B2*w, w2**2)
return [A1, A2, wrap2pi(ph1), wrap2pi(ph2)]
}
const m1g = p1.group("m1")
p1.line({
x:()=>[0,2.15],
y:()=>[posM1 + dispM1,posM1 + dispM1],
group: m1g,
strokeLinecap: "square",
strokeWidth: 2,
})
p1.rect({
position: ()=>[0,posM1 + dispM1],
size: ()=>[2.1 + 2*reg.m1.ratio,heightM],
corner: 10,
group: m1g,
})
p1.text({
position: ()=>[0,posM1 + dispM1],
text: "m\\sub{1}",
group: m1g,
color: color.textInv
})
const m2g = p1.group("m2")
p1.line({
x:()=>[0,2.15],
y:()=>[posM2 + dispM2,posM2 + dispM2],
group: m2g,
stroke: color.second,
strokeLinecap: "square",
strokeWidth: 2,
})
p1.rect({
position: ()=>[0,posM2 + dispM2],
size: ()=>[2.1 + 2*reg.m2.ratio,heightM],
corner: 10,
fill: color.second,
group: m2g,
})
p1.text({
position: ()=>[0,posM2 + dispM2],
text: "m\\sub{2}",
group: m2g,
color: color.textInv
})
const s1g = p1.group("s1")
const s1Spr = p1.spring({
start:()=>[-1,posS1],
end:()=>[-1,posM1 - heightM/2 + dispM1],
coils:6,
diameter:1.5,
group:s1g,
stroke:color.neutral
})
p1.rect({
position: ()=>[-2.75,(posS1 + posM1 + dispM1)/2],
size: ()=>[p1.fromXsize(50),p1.fromYsize(50)],
anchor: [0,0.5],
corner: 10,
group: s1g,
opacity:0.25,
stroke:color.neutral,
})
p1.text({
position: ()=>[-2.75 + p1.fromXsize(50)/2,(-5 + posM1 + dispM1)/2],
text: "s\\sub{1}",
group: s1g,
})
const s2g = p1.group("s2")
const s2Spr = p1.spring({
start:()=>[-1,posM1 + heightM/2 + dispM1],
end:()=>[-1,posM2 + dispM2 - heightM/2],
coils:6,
diameter:1.5,
group:s2g,
stroke:color.neutral
})
p1.rect({
position: ()=>[-2.75,((posM1 + heightM/2 + dispM1) + (posM2 + dispM2 - heightM/2))/2],
size: ()=>[p1.fromXsize(50),p1.fromYsize(50)],
anchor: [0,0.5],
corner: 10,
group: s2g,
opacity:0.25,
stroke:color.neutral,
})
p1.text({
position: ()=>[-2.75 + p1.fromXsize(50)/2,((posM1 + heightM/2 + dispM1) + (posM2 + dispM2 - heightM/2))/2],
text: "s\\sub{2}",
group: s2g,
})
const r1g = p1.group("r1")
p1.damper({
start:()=>[1,posS1],
end:()=>[1,posM1 - heightM/2 + dispM1],
gap:()=>(1-reg.r1.ratio**0.5),
atRatio:()=>0.5,
diameter:1,
group:r1g,
stroke:color.neutral
})
p1.rect({
position: ()=>[2,(posS1 + posM1)*(4/5)],
size: ()=>[p1.fromXsize(50),p1.fromYsize(50)],
anchor: [0,0.5],
corner: 10,
group: r1g,
opacity:0.25,
stroke:color.neutral,
})
p1.text({
position: ()=>[2 + p1.fromXsize(50)/2,(posS1 + posM1)*(4/5)],
text: "r\\sub{1}",
group: r1g,
})
const r2g = p1.group("r2")
p1.damper({
start:()=>[1,posM1 + heightM/2 + dispM1],
end:()=>[1,posM2 + dispM2 - heightM/2],
gap:()=>(1-reg.r1.ratio**0.5),
atRatio:()=>0.5,
diameter:1,
group:r2g,
stroke:color.neutral
})
p1.rect({
position: ()=>[1.5,((posM1 + heightM/2 + dispM1) + (posM2 + dispM2 - heightM/2))/2],
size: ()=>[p1.fromXsize(50),p1.fromYsize(50)],
anchor: [0,0.5],
corner: 10,
group: r2g,
opacity:0.25,
stroke:color.neutral,
})
p1.text({
position: ()=>[1.5 + p1.fromXsize(50)/2,((posM1 + heightM/2 + dispM1) + (posM2 + dispM2 - heightM/2))/2],
text: "r\\sub{2}",
group: r2g,
})
const frf = p2.group("frf")
p2.plotFn({
y: f => {
const [A1,A2,ph1,ph2] = ampPhase(f,reg.m1.value,reg.s1.value,reg.r1.value,reg.m2.value,reg.s2.value,reg.r2.value)
return A2
},
group: frf,
stroke: color.second,
})
p2.plotFn({
y: f => {
const [A1,A2,ph1,ph2] = ampPhase(f,reg.m1.value,reg.s1.value,reg.r1.value,reg.m2.value,reg.s2.value,reg.r2.value)
return A1
},
group: frf,
})
const frfPh = p3.group("frfPh")
const frfPh2 = p3.plotFn({
y: f => {
const [A1,A2,ph1,ph2] = ampPhase(f,reg.m1.value,reg.s1.value,reg.r1.value,reg.m2.value,reg.s2.value,reg.r2.value)
return ph2
},
group: frfPh,
stroke: color.second,
})
const frfPh1 = p3.plotFn({
y: f => {
const [A1,A2,ph1,ph2] = ampPhase(f,reg.m1.value,reg.s1.value,reg.r1.value,reg.m2.value,reg.s2.value,reg.r2.value)
return ph1
},
group: frfPh,
})
const frfPhDiff = p3.plotFn({
y: f => {
const [A1,A2,ph1,ph2] = ampPhase(f,reg.m1.value,reg.s1.value,reg.r1.value,reg.m2.value,reg.s2.value,reg.r2.value)
return ph2 - ph1
},
group: frfPh,
})
frfPhDiff.classList.add("hidden")
const freqMarks = p3.group("freqMarks")
p2.line({
x: ()=>[f0,f0],
y: ()=>[p2.fromY(p3.toY(p3.y.min)),p2.y.max],
group: freqMarks,
strokeWidth: 2,
clip: false,
dash: "20 5"
})
p2.text({
clip: false,
text: "\\f\\sub{0}",
color: color.main,
group: freqMarks,
position: ()=>[f0,4.5],
opaque: true,
})
p2.line({
x: ()=>[f1,f1],
y: ()=>[p2.fromY(p3.toY(p3.y.min)),p2.y.max],
group: freqMarks,
strokeWidth: 2,
clip: false,
dash: "20 5"
})
p2.text({
clip: false,
text: "\\f\\sub{1}",
color: color.main,
group: freqMarks,
position: ()=>[f1,4.5],
opaque: true,
})
p2.line({
x: ()=>[f_1,f_1],
y: ()=>[p2.fromY(p3.toY(p3.y.min)),p2.y.max],
group: freqMarks,
strokeWidth: 2,
clip: false,
dash: "20 5",
stroke: color.third
})
p2.text({
clip: false,
text: "\\f\\sub{-1}",
color: color.third,
group: freqMarks,
position: ()=>[f_1,4.5],
opaque: true,
})
const force = p1.group("force")
reg.showForce.value = true
p1.arrow({
start:()=>[2.1,posM1 + dispM1],
end:()=>[2.1,posM1 + dispM1 + fLength],
size:()=>[0.2,0.3],
fill:color.highlight,
stroke:color.highlight,
strokeWidth:5,
group: force,
})
p2.line({
x: ()=>[reg.freq.value,reg.freq.value],
y: ()=>[p2.fromY(p3.toY(p3.y.min)),p2.y.max],
stroke: color.highlight,
group: freqMarks,
strokeWidth: 2,
clip: false,
})
p2.circle({
position: ()=>[reg.freq.value,ampM1],
radius: 6,
fill: color.highlight,
group: freqMarks,
})
p2.circle({
position: ()=>[reg.freq.value,ampM2],
radius: 4,
fill: color.highlight,
group: freqMarks,
})
p3.circle({
position: ()=>[reg.freq.value,!reg.phaseType.value?phaseM1:phaseM2-phaseM1],
radius: 6,
fill: color.highlight,
group: freqMarks,
})
p3.circle({
position: ()=>[reg.freq.value,!reg.phaseType.value?phaseM2:-1],
radius: 4,
fill: color.highlight,
group: freqMarks,
})
init = true