Resonant
Frequencies
\f\sub{0} = \reg{}{f0} [Hz]
\f\sub{1} = \reg{}{f1} [Hz]
Massm\sub{1} [ kg ]
Springs\sub{1} [ \ratio{N}{m} ]
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} ]
Displacement
Amplitudes
\delta\sub{A\sub{1}} = \reg{}{m1-amp} [m]
\delta\sub{A\sub{2}} = \reg{}{m2-amp} [m]
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.bottom+100],
size: [this.width - spaceWidth - 100 - 150 - 20 ,this.height-100-40-40 ],
rangeX: [0,2],
rangeY: [-5,5],
labelX: "Driving Frequency \\f [Hz]",
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)",
position:[p2x+p2w/2,p2y+p2h+20],
anchor:[0.5,0],
screen: true,
clip: false,
fontSize: 40,
})
let ampM1, dispM1, ampM2, dispM2, fLength, f0, f1
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()
m2g.update()
s2g.update()
force.update()
})
reg.freq.onUpdate(()=>updateAll())
reg.m1.onUpdate(()=>updateAll())
reg.s1.onUpdate(()=>updateAll())
reg.m2.onUpdate(()=>updateAll())
reg.s2.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"
})
function updateAll(){
updateResponse()
updatePosition()
updateGroups()
}
function updateGroups(){
m1g.update()
s1g.update()
m2g.update()
s2g.update()
force.update()
frf.update()
driveMark.update()
}
function updateResponse(){
const m1 = reg.m1.value
const s1 = reg.s1.value
const m2 = reg.m2.value
const s2 = reg.s2.value
const f = reg.freq.value
const fT = frequency(m1,s1,m2,s2)
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)
const [A1,A2] = amplitude(f,m1,s1,m2,s2)
ampM1 = A1
ampM2 = A2
reg.m1Amp.textContent = format(ampM1,2)
reg.m2Amp.textContent = format(ampM2,2)
if(init) s1Spr.style.strokeWidth = 1 + 4*reg.s1.ratio
if(init) 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)
dispM2 = ampM2*cos(T)
}
updateResponse()
updatePosition()
function frequency(m1, s1, m2, s2){
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 amplitude(f,m1,s1,m2,s2){
const w = pi2*f
const den = ( s1 + s2 - m1*w**2 )*( m2*w**2 - s2 ) + s2**2
const A1 = ( ampF *( m2*w**2 - s2 )) / den
const A2 = -ampF * s2 / den
return [A1,A2]
}
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("m1")
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:()=>[0,posS1],
end:()=>[0,posM1 - heightM/2 + dispM1],
coils:6,
diameter:1.5,
group:s1g,
stroke:color.neutral
})
p1.rect({
position: ()=>[-2,(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 + p1.fromXsize(50)/2,(-5 + posM1 + dispM1)/2],
text: "s\\sub{1}",
group: s1g,
})
const s2g = p1.group("s2")
const s2Spr = p1.spring({
start:()=>[0,posM1 + heightM/2 + dispM1],
end:()=>[0,posM2 + dispM2 - heightM/2],
coils:6,
diameter:1.5,
group:s2g,
stroke:color.neutral
})
p1.rect({
position: ()=>[-2,((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 + p1.fromXsize(50)/2,((posM1 + heightM/2 + dispM1) + (posM2 + dispM2 - heightM/2))/2],
text: "s\\sub{2}",
group: s2g,
})
const frf = p2.group("frf")
const err = 0.001
p2.plotFn({
y: f => {
const [A1,A2] = amplitude(f,reg.m1.value,reg.s1.value,reg.m2.value,reg.s2.value)
return A2
},
max: ()=>f0-err,
threshold:[-5,5],
group: frf,
stroke: color.second
})
p2.plotFn({
y: f => {
const [A1,A2] = amplitude(f,reg.m1.value,reg.s1.value,reg.m2.value,reg.s2.value)
return A2
},
min: ()=>f0+err,
max: ()=>f1-err,
threshold:[-5,5],
group: frf,
stroke: color.second
})
p2.plotFn({
y: f => {
const [A1,A2] = amplitude(f,reg.m1.value,reg.s1.value,reg.m2.value,reg.s2.value)
return A2
},
min: ()=>f1+err,
threshold:[-5,5],
group: frf,
stroke: color.second
})
p2.plotFn({
y: f => {
const [A1,A2] = amplitude(f,reg.m1.value,reg.s1.value,reg.m2.value,reg.s2.value)
return A1
},
max: ()=>f0-err,
threshold:[-5,5],
group: frf,
})
p2.plotFn({
y: f => {
const [A1,A2] = amplitude(f,reg.m1.value,reg.s1.value,reg.m2.value,reg.s2.value)
return A1
},
min: ()=>f0+err,
max: ()=>f1-err,
threshold:[-5,5],
group: frf,
})
p2.plotFn({
y: f => {
const [A1,A2] = amplitude(f,reg.m1.value,reg.s1.value,reg.m2.value,reg.s2.value)
return A1
},
min: ()=>f1+err,
threshold:[-5,5],
group: frf,
})
p2.line({
x: ()=>[f0,f0],
y: ()=>[p2.y.min,p2.y.max],
group: frf,
strokeWidth: 2,
clip: false,
dash: "20 5"
})
p2.text({
clip: false,
text: "\\f\\sub{0}",
color: color.main,
group: frf,
position: ()=>[f0,4.5],
opaque: true,
})
p2.line({
x: ()=>[f1,f1],
y: ()=>[p2.y.min,p2.y.max],
group: frf,
strokeWidth: 2,
clip: false,
dash: "20 5"
})
p2.text({
clip: false,
text: "\\f\\sub{1}",
color: color.main,
group: frf,
position: ()=>[f1,4.5],
opaque: true,
})
p2.line({
x: ()=>[f_1,f_1],
y: ()=>[p2.y.min,p2.y.max],
group: frf,
strokeWidth: 2,
clip: false,
dash: "20 5",
stroke: color.third
})
p2.text({
clip: false,
text: "\\f\\sub{-1}",
color: color.third,
group: frf,
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,
})
const driveMark = p2.group("mark")
p2.line({
x: ()=>[reg.freq.value,reg.freq.value],
y: ()=>[p2.y.min,p2.y.max],
stroke: color.highlight,
group: driveMark,
strokeWidth: 2,
clip: false,
})
p2.circle({
position: ()=>[reg.freq.value,ampM1],
radius: 6,
fill: color.highlight,
group: driveMark,
})
p2.circle({
position: ()=>[reg.freq.value,ampM2],
radius: 4,
fill: color.highlight,
group: driveMark,
})
init = true