138 lines
3.1 KiB
TypeScript
138 lines
3.1 KiB
TypeScript
import { Plot } from '../plot/mod.ts'
|
|
import { range } from '../utils/list.ts'
|
|
|
|
const distance = 200 //cm
|
|
const size = 300 //cm
|
|
const gap = 2.6 //cm
|
|
const radius = 1 //cm
|
|
|
|
const markerSize = 2 * radius * 4 //4: px aspect factor
|
|
|
|
function getPoints() {
|
|
const plot = {
|
|
x: [] as number[],
|
|
y: [] as number[],
|
|
mode: 'markers',
|
|
name: 'references',
|
|
marker: { size: markerSize },
|
|
}
|
|
|
|
const step = 20 * gap
|
|
for (const x of range(0, size + 1, step)) {
|
|
for (const y of range(0, size + 1, step)) {
|
|
plot.x.push(x)
|
|
plot.y.push(y)
|
|
}
|
|
}
|
|
|
|
return plot
|
|
}
|
|
|
|
function computePoints(
|
|
samples: number,
|
|
options = { static: true, dynamic: true, rounding: true },
|
|
) {
|
|
const label = [
|
|
options.static ? 'static' : undefined,
|
|
options.dynamic ? 'dyn.' : undefined,
|
|
options.rounding ? 'round.' : undefined,
|
|
].filter((v) => v).join('+')
|
|
|
|
const plot = {
|
|
x: [] as number[],
|
|
y: [] as number[],
|
|
mode: 'markers',
|
|
name: `simulation (${label})`,
|
|
marker: { size: markerSize },
|
|
opacity: 0.3,
|
|
}
|
|
|
|
const step = 20 * gap
|
|
for (const x of range(0, size + 1, step)) {
|
|
for (const y of range(0, size + 1, step)) {
|
|
//Projected coordinates
|
|
const phi = Math.atan(x / distance)
|
|
const theta = Math.atan(y / distance)
|
|
|
|
for (const _ of range(0, samples)) {
|
|
// Static errors
|
|
const laserAxisError = () => Math.random() * 3 - 3 / 2
|
|
const staticErrors = () => laserAxisError()
|
|
|
|
// Dynamic errors
|
|
const headMotorsError = () => Math.random() * 0.02 - 0.01
|
|
const headOrbitError = () => Math.random() * 0.2 - 0.1
|
|
const dynamicErrors = () => headMotorsError() + headOrbitError()
|
|
|
|
// Code rounding
|
|
const codeThetaRound = () => Math.random() * 4.39 - 4.39 / 2
|
|
const codePhiRound = () => Math.random() * 6.43 - 6.43 / 2
|
|
|
|
const thetaErr = (options.static && staticErrors()) +
|
|
(options.dynamic && dynamicErrors()) +
|
|
(options.rounding && codeThetaRound())
|
|
|
|
const phiErr = (options.static && staticErrors()) +
|
|
(options.dynamic && dynamicErrors()) +
|
|
(options.rounding && codePhiRound())
|
|
|
|
//Cartesian coordinates
|
|
const x1 = Math.tan(phi + toRad(phiErr)) * distance
|
|
const y1 = Math.tan(theta + toRad(thetaErr)) * distance
|
|
|
|
plot.x.push(x1)
|
|
plot.y.push(y1)
|
|
}
|
|
}
|
|
}
|
|
|
|
return plot
|
|
}
|
|
|
|
function getHoles() {
|
|
const plot = {
|
|
x: [] as number[],
|
|
y: [] as number[],
|
|
mode: 'markers',
|
|
marker: {
|
|
size: markerSize,
|
|
color: 'rgba(0, 0, 0, 0)',
|
|
line: {
|
|
color: 'rgba(0, 0, 0, 1)',
|
|
width: 1,
|
|
},
|
|
},
|
|
opacity: 0.3,
|
|
name: 'holes',
|
|
}
|
|
|
|
let shift = false
|
|
|
|
for (const x of range(0, size, gap)) {
|
|
for (const y of range(0, size, gap)) {
|
|
plot.x.push(shift ? x + gap / 2 : x)
|
|
plot.y.push(y)
|
|
shift = !shift
|
|
}
|
|
}
|
|
|
|
return plot
|
|
}
|
|
|
|
const plot = new Plot()
|
|
|
|
plot.plot([
|
|
computePoints(1e3, { static: true, dynamic: true, rounding: true }),
|
|
computePoints(1e3, { static: true, dynamic: true, rounding: false }),
|
|
computePoints(1e3, { static: false, dynamic: true, rounding: false }),
|
|
getPoints(),
|
|
getHoles(),
|
|
], {
|
|
xaxis: { range: [-10, size + 10] },
|
|
yaxis: { range: [-10, size + 10] },
|
|
})
|
|
|
|
function toRad(degree: number): number {
|
|
return degree * Math.PI / 180
|
|
}
|