technoshop.essais_mesure_fi.../measure_clean_compa_3.ts
2025-07-21 10:56:11 +02:00

202 lines
4.9 KiB
TypeScript

import { Data as PlotlyData, Plot } from './plot/mod.ts'
type Data = {
timestamp: string //ISO string
volume: number //mL
ipam: number //ipam
record: {
pression: number //bar
mass: number //g
timestamp: number //s
}[]
}
function parseDatas(file: string): Data {
const lines = file.split('\n')
lines.shift() //remove title
const [day, month, year, time] = lines.shift()!.split(' ')
const monthList = [
'_',
'janv.',
'fev.',
'mars',
'avril',
'mai',
'juin',
'juil.',
'aout',
'sept.',
'dec.',
]
const timestamp = `${year}-${
monthList.indexOf(month).toString().padStart(2, '0')
}-${day.padStart(2, '0')}T${time}`
lines.shift() //remove empty line
const volume = lines.shift()!.split(';').slice(1).map(Number).at(0)!
const ipam = lines.shift()!.split(';').slice(1).map(Number).at(0)!
lines.shift() //remove empty line
lines.shift() //remove title
const record = lines
.map((line) => line.split(';').slice(1).map(Number))
.map(([pression, mass, timestamp]) => ({ pression, mass, timestamp }))
return {
timestamp,
volume,
ipam,
record,
}
}
function filterRecord(record: Data['record'], ceil = 0.4): Data['record'] {
const filteredRecord: Data['record'] = []
record.forEach((value, index) => {
if (index === 0) filteredRecord.push(value)
if (value.mass < 10) filteredRecord.push(value)
const previousMass = filteredRecord.at(-1)?.mass ?? value.mass
const delta = Math.abs(value.mass - previousMass) / value.mass
if (delta >= ceil) filteredRecord.push({ ...value, mass: previousMass })
else filteredRecord.push(value)
})
return filteredRecord
}
function cutRecord(record: Data['record']): Data['record'] {
let minMass = record[0].mass
let minMassIndex = 0
for (const value of record) {
minMassIndex++
if (value.mass < minMass) minMass = value.mass
if (value.mass > minMass) break
}
return record.slice(minMassIndex)
}
function lowpass(array: number[], window: number): number[] {
return array.slice(0, -window)
.map(
(_, index) =>
array.slice(index, index + window).reduce(
(prev, curr) => prev + curr / window,
0,
),
)
}
function derivative(x: number[], y: number[]): number[] {
return y.slice(0, -1).map((_, index) =>
(y[index + 1] - y[index]) / (x[index + 1] - x[index])
)
}
function getDatasPlot(datas: Data) {
const record = cutRecord(filterRecord(datas.record))
// const minMassIndex = record.findIndex((value) => )
// const record2 = record.filter(({mass}) => mass)
const x = record.map((
{ timestamp },
) => (timestamp - datas.record[0].timestamp)).map((x) => x / 6e3)
const y = record.map((data) => data.mass)
.map((y) => y / record.slice(-2, -1)[0].mass)
.map((y) => 1 - y)
.map(Math.log)
const meta = `${datas.volume}mL ${datas.ipam}IPAM ${
datas.timestamp.split('T')[1]
}`
const { ipam } = datas
// const dY = lowpass(derivative(x, lowpass(y, 100)), 100)
const dY = lowpass(derivative(x, y), 100)
const xMinIndex = x.findIndex((xi) => xi > 5)
const xMaxIndex = x.findIndex((xi) => xi > 10)
const expSlope = dY.slice(
xMinIndex,
xMaxIndex,
).reduce((prev, curr, _, array) => prev + curr / array.length, 0)
const yRaw = record.map((data) => data.mass)
const yMax = yRaw.reduce((max, curr) => curr > max ? curr : max)
const linearY = x.map((_, index) =>
yRaw[index] - (yMax * (1 - Math.exp(expSlope * x[index] * 6)))
)
const fitY = x.map((_, index) =>
yMax * (1 - Math.exp(expSlope * x[index] * 6))
)
const yLinDeriv = derivative(x, linearY)
const ipamSlope = yLinDeriv.slice(xMinIndex, xMaxIndex).reduce(
(p, c, _, a) => p + c / a.length,
0,
) * 6
console.log({
ipam,
ipamSlope: ipamSlope.toFixed(2),
expSlope: expSlope.toFixed(4),
})
console.log(x.at(-2))
const isHaut = datas.timestamp.includes('07-08')
const color = `rgb(${isHaut ? 255 : 0}, ${255 ** (ipam / 47)}, ${
255 - 255 ** (ipam / 47)
})`
console.log(color)
return ([
{
x: x,
// y: dY,
// y: linearY,
// y: lowpass(linearY, 200),
// y: lowpass(yLinDeriv, 200).map((y) => Math.abs(y)),
// y: fitY,
y: yRaw,
name: `mass (${meta}) ${isHaut ? 'réservoir haut' : 'réservoir bas'}`,
line: { color },
},
// {
// x: x,
// y: dY,
// yaxis: 'y2',
// name: `fit (${meta})`,
// line: {
// color: `rgb(${50 * Math.log(ipam)}, 127, ${255 - 50 * Math.log(ipam)})`,
// },
// },
] satisfies Partial<PlotlyData>[])
}
const plot = new Plot()
const plots: Partial<PlotlyData>[] = []
for await (const file of Deno.readDir('./datas')) {
if (!file.isFile) continue
if (!file.name.match(/^Rauzan_\d{2}-\d{2}_\d{2}-\d{2}\.csv$/)) continue
const path = `./datas/${file.name}`
const csv = await Deno.readTextFile(path)
const datas = parseDatas(csv)
if (Number.isNaN(datas.ipam)) continue
const plotDatas = getDatasPlot(datas)
plots.push(...plotDatas)
}
plot.plot(plots, {
xaxis: { title: { text: 'timestamp [min/10]' } },
yaxis: { title: { text: 'mass [g]' } },
// yaxis2: { title: { text: 'fit' }, side: 'right' },
})