Natural Frequency
\f\sub{0} = \frac{1}{2\pi} \tall{(}{170} \frac{s}{m} \tall{)}{170}\sup{1/2}
\f\sub{0} = \reg{}{natFreq} Hz
Mass 1m [ kg ]
Spring 1s [ \ratio{N}{m} ]
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]
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("force","orange")
color.add("mark","orange")
const spaceWidth = this.width/4 - 100
const p1 = this.newPlot({
position: [ this.left+100, this.bottom+20 ],
size: [spaceWidth, this.height-40],
rangeX: [-1.1,1.5],
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+100],
size: [this.width - spaceWidth - 100 - 150 - 20 ,this.height-100-40 ],
rangeX: [0,2],
rangeY: [-5,5],
labelX: "Driving Frequency \\f [Hz]",
labelY: "Displacement Amplitude \\delta\\sub{A} [m]",
tickStepX: 0.5,
labelFontSizeY: 40,
labelFontSizeX: 40,
})
let ampM1, dispM1, fLength, natFreq
const posM1 = 0
const posS1 = -5
const heightM = 1
const ampF = 50
reg.osc.onUpdate(t=>{
updatePosition()
m1.update()
s1.update()
force.update()
})
reg.freq.onUpdate(f=>{
updateResponse()
updatePosition()
force.update()
frf.update()
driveMark.update()
m1.update()
s1.update()
})
reg.m1.onUpdate(v=>{
updateResponse()
m1.update()
frf.update()
driveMark.update()
})
reg.s1.onUpdate((v,p,el)=>{
updateResponse()
s1.update()
frf.update()
driveMark.update()
s1Spr.style.strokeWidth = 1 + 6*el.ratio
})
reg.showForce.onUpdate(v=>{
force.classList.toggle("hidden",!v)
reg.showForce.button.style.background = !v?"":color.force
})
function updateResponse(){
reg.forceAmplitude.textContent = ampF
natFreq = sqrt(reg.s1.value/reg.m1.value) / pi2
reg.natFreq.textContent = format(natFreq,2)
ampM1 = amplitude(reg.freq.value)
reg.m1Amp.textContent = format(ampM1,2)
}
updateResponse()
function updatePosition(){
const T = pi2*reg.freq.value*reg.osc.value
fLength = cos(T)
dispM1 = ampM1*cos(T)
}
updatePosition()
function amplitude(f){
return ( ampF / reg.m1.value ) / ( (natFreq*pi2)**2 - (f*pi2)**2 )
}
const m1 = p1.group("m1")
const m1Rect = p1.rect({
position: ()=>[0,posM1 + dispM1],
size: ()=>[0.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 s1 = p1.group("s1")
const s1Spr = p1.spring({
start:()=>[0,posS1],
end:()=>[0,posM1 - heightM/2 + dispM1],
coils:6,
diameter:1,
group:s1,
stroke:color.spring,
strokeWidth:1 + 6*reg.s1.ratio,
})
p1.rect({
position: ()=>[-0.8,(posS1 + posM1 + dispM1)/2],
size: ()=>[p1.fromXsize(50),p1.fromYsize(50)],
fill: "red",
corner: 10,
group: s1,
fill: color.mass,
opacity:0.25
})
p1.text({
position: ()=>[-0.8,(-5 + posM1 + dispM1)/2],
text: "s",
group: s1,
color: color.springText
})
const force = p1.arrow({
start:()=>[1,posM1 + heightM/2 + dispM1],
end:()=>[1,posM1 + heightM/2 + dispM1 + fLength],
size:()=>1/2,
fill:color.force,
stroke:color.force,
strokeWidth:5
})
reg.showForce.value = true
const frf = p2.group("frf")
p2.plotFn({
y: f => {
const A = amplitude(f)
return A >= 0 ? A : 20e3
},
group: frf,
})
p2.plotFn({
y: f => {
const A = amplitude(f)
return A <= 0 ? A : -20e3
},
group: frf,
})
const driveMark = p2.group("mark")
p2.line({
x: ()=>[reg.freq.value,reg.freq.value],
y: ()=>[p2.y.min,p2.y.max],
stroke: color.mark,
group: driveMark,
strokeWidth: 1,
})
const test = p2.circle({
position: ()=>[reg.freq.value,ampM1],
radius: 6,
fill: color.mark,
stroke: color.mark,
group: driveMark,
})