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[]) } const plot = new Plot() const plots: Partial[] = [] 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' }, })