import { Data as PlotlyData, Plot } from './plot/mod.ts' import { curveFit } from './utils/fit.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 = [ '_', 'janvier', 'fevrier', 'mars', 'avril', 'mai', 'juin', 'juillet', 'aout', 'septembre', 'decembre', ] 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 pickLess(array: number[], step: number): number[] { const result: number[] = [] for (let i = 0; i < array.length; i += step) { result.push(array[i]) } return result } function getDatasPlot(datas: Data) { const record = cutRecord(filterRecord(datas.record)) const x = record.map(( { timestamp }, ) => (timestamp - datas.record[0].timestamp)).map((x) => x / 6e3) const y = record.map((data) => data.mass) const meta = `${datas.volume}mL ${datas.ipam}IPAM ${ datas.timestamp.split('T')[1] }` const yLess = pickLess(y, 2) const xLess = pickLess(x, 2) const { ipam } = datas const yMax = yLess.reduce((max, curr) => curr > max ? curr : max) const fitFn = (x: number, [a, b]: [number, number]) => yMax * (1 - Math.exp(-a * x) + b * x) const fit = curveFit(xLess, yLess, fitFn, [0.2, 0.005]) console.log(fit.params) console.log({ ipam, }) return ([ { x: xLess, y: yLess, name: `mass (${meta})`, line: { color: `rgb(${50 * Math.log(ipam)}, 0, ${255 - 50 * Math.log(ipam)})`, }, }, { x: xLess, y: xLess.map((x) => fitFn(x, fit.params)), // y: xLess.map((x) => fitFn(x, [0.5, 0.01])), yaxis: 'y2', name: `fit (${meta})`, line: { color: `rgb(${50 * Math.log(ipam)}, 127, ${255 - 50 * Math.log(ipam)})`, }, }, ] satisfies Partial[]) } const plot = new Plot() const plots: Partial[] = [] for await (const file of Deno.readDir('./datas')) { if (!file.isFile) continue if (!file.name.match(/Vrr3_\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 (datas.ipam !== 4) continue if (Number.isNaN(datas.ipam)) continue const plotDatas = getDatasPlot(datas) plots.push(...plotDatas) } plot.plot(plots, { xaxis: { title: { text: 'timestamp' } }, yaxis: { title: { text: 'mass [g]' } }, yaxis2: { title: { text: 'fit' }, side: 'right' }, }) //RMD 4005 x2