add MLX test; not fully works yet, try to find my error in calculations due to datashit

This commit is contained in:
Edward Emelianov 2025-09-09 23:06:59 +03:00
parent 664566aa47
commit dfdc4222fd
23 changed files with 1066 additions and 0 deletions

58
MLX90640_test/Makefile Normal file
View File

@ -0,0 +1,58 @@
# run `make DEF=...` to add extra defines
PROGRAM := mlx
LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all
LDFLAGS += -lusefull_macros -L/usr/local/lib -lm -flto
SRCS := $(wildcard *.c)
DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111
OBJDIR := mk
CFLAGS += -O2 -Wall -Wextra -Wno-trampolines -std=gnu99 -flto
OBJS := $(addprefix $(OBJDIR)/, $(SRCS:%.c=%.o))
DEPS := $(OBJS:.o=.d)
TARGFILE := $(OBJDIR)/TARGET
CC = gcc
#TARGET := RELEASE
ifeq ($(shell test -e $(TARGFILE) && echo -n yes),yes)
TARGET := $(file < $(TARGFILE))
else
TARGET := RELEASE
endif
ifeq ($(TARGET), DEBUG)
.DEFAULT_GOAL := debug
endif
release: $(PROGRAM)
debug: CFLAGS += -DEBUG -Werror
debug: TARGET := DEBUG
debug: $(PROGRAM)
$(TARGFILE): $(OBJDIR)
@echo -e "\t\tTARGET: $(TARGET)"
@echo "$(TARGET)" > $(TARGFILE)
$(PROGRAM) : $(TARGFILE) $(OBJS)
@echo -e "\t\tLD $(PROGRAM)"
$(CC) $(OBJS) $(LDFLAGS) -o $(PROGRAM)
$(OBJDIR):
@mkdir $(OBJDIR)
ifneq ($(MAKECMDGOALS),clean)
-include $(DEPS)
endif
$(OBJDIR)/%.o: %.c
@echo -e "\t\tCC $<"
$(CC) -MD -c $(LDFLAGS) $(CFLAGS) $(DEFINES) -o $@ $<
clean:
@echo -e "\t\tCLEAN"
@rm -rf $(OBJDIR) 2>/dev/null || true
xclean: clean
@rm -f $(PROGRAM)
.PHONY: clean xclean

2
MLX90640_test/Readme Normal file
View File

@ -0,0 +1,2 @@
device-independent MLX90640 processing & test
based on data example from melexis

1
MLX90640_test/alpha.csv Normal file

File diff suppressed because one or more lines are too long

1
MLX90640_test/eeprom.csv Normal file

File diff suppressed because one or more lines are too long

1
MLX90640_test/frame0.csv Normal file

File diff suppressed because one or more lines are too long

1
MLX90640_test/frame1.csv Normal file

File diff suppressed because one or more lines are too long

1
MLX90640_test/kta.csv Normal file

File diff suppressed because one or more lines are too long

1
MLX90640_test/kv.csv Normal file

File diff suppressed because one or more lines are too long

41
MLX90640_test/main.c Normal file
View File

@ -0,0 +1,41 @@
/*
* This file is part of the mlxtest project.
* Copyright 2022 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <math.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <usefull_macros.h>
#include "mlx90640.h"
#include "testdata.h"
int main (int _U_ argc, char _U_ **argv){
sl_init();
MLX90640_params p;
if(!get_parameters(EEPROM, &p)) ERRX("Can't get parameters from test data");
dump_parameters(&p, &extracted_parameters);
for(int i = 0; i < 2; ++i){
printf("Process subpage %d\n", i);
fp_t *sp = process_subpage(&p, FRAME0, i, 2);
if(!sp) ERRX("WTF?");
dumpIma(sp);
chkImage(sp, ToFrame[i]);
}
return 0;
}

487
MLX90640_test/mlx90640.c Normal file
View File

@ -0,0 +1,487 @@
/*
* This file is part of the mlxtest project.
* Copyright 2022 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <usefull_macros.h>
#include "mlx90640.h"
#include "mlx90640_regs.h"
// tolerance of floating point comparison
#define FP_TOLERANCE (1e-3)
static fp_t mlx_image[MLX_PIXNO] = {0}; // ready image
#ifdef EBUG
static double Tlast = 0.;
#define chstate() do{Tlast = sl_dtime(); DBG("chstate()");}while(0)
#endif
void dumpIma(const fp_t im[MLX_PIXNO]){
for(int row = 0; row < MLX_H; ++row){
for(int col = 0; col < MLX_W; ++col){
printf("%5.1f ", *im++);
}
printf("\n");
}
}
static void chki(const char *name, int16_t param, int16_t standard){
printf("%*s | %-16d | %-16d - ", -16, name, param, standard);
if(param != standard){
red("NOT equal!\n"); exit(1);
}
printf("OK\n");
}
static void chkf(const char *name, fp_t param, fp_t standard){
printf("%*s | %-16g | %-16g - ", -16, name, param, standard);
fp_t diff = (fabs(param) + fabs(standard)) * FP_TOLERANCE;
if(fabs(param - standard) > diff){
// DBG("diff = %g", diff);
red("NOT equal!\n"); exit(1);
}
printf("OK\n");
}
static void chkfa(const char *name, const fp_t *ap, const fp_t *as, int n){
char buf[16];
snprintf(buf, 15, "(size %d)", n);
printf("%*s | %-16s | %-16s - ", -16, name, "(array)", buf);
for(int i = 0; i < n; ++i){
fp_t diff = (fabs(as[i]) + fabs(ap[i])) * FP_TOLERANCE;
if(fabs(ap[i] - as[i]) > diff){
// DBG("diff = %g", diff);
red("NOT equal on index %d (%g and %g)\n", i, ap[i], as[i]);
exit(1);
}
}
printf("OK");
if(n < 10){
for(int i = 0; i < n; ++i) printf(" %g", as[i]);
}
printf("\n");
}
static void chku8a(const char *name, const uint8_t *ap, const uint8_t *as, int n){
char buf[16];
snprintf(buf, 15, "(size %d)", n);
printf("%*s | %-16s | %-16s - ", -16, name, "(array)", buf);
for(int i = 0; i < n; ++i){
if(ap[i] != as[i]){
red("NOT equal on index %d (%d and %d)\n", i, ap[i], as[i]);
exit(1);
}
}
printf("OK");
if(n < 10){
for(int i = 0; i < n; ++i) printf(" %d", as[i]);
}
printf("\n");
}
void chkImage(const fp_t Image[MLX_PIXNO], const fp_t ToFrame[MLX_PIXNO]){
chkfa("Image", Image, ToFrame, MLX_PIXNO);
}
void dump_parameters(MLX90640_params *params, const MLX90640_params *standard){
printf("###########################################################\n");
printf("%*s | %*s | %*s\n###########################################################\n",
-16, "# name", -16, "value", -16, "standard");
#define CHKI(f) do{chki(#f, params->f, standard->f);}while(0)
#define CHKF(f) do{chkf(#f, params->f, standard->f);}while(0)
#define CHKFA(f, n) do{chkfa(#f, params->f, standard->f, n);}while(0)
#define CHKU8A(f, n) do{chku8a(#f, params->f, standard->f, n);}while(0)
CHKI(kVdd);
CHKI(vdd25);
CHKF(KvPTAT);
CHKI(vPTAT25);
CHKF(alphaPTAT);
CHKI(gainEE);
CHKF(tgc);
CHKF(cpKv);
CHKF(cpKta);
CHKF(KsTa);
CHKFA(CT, 3);
CHKFA(KsTo, 4);
CHKFA(alpha, MLX_PIXNO);
CHKFA(offset, MLX_PIXNO);
CHKFA(kta, MLX_PIXNO);
CHKFA(kv, 4);
CHKFA(cpAlpha, 2);
CHKI(resolEE);
CHKI(cpOffset[0]); CHKI(cpOffset[1]);
CHKU8A(outliers, MLX_PIXNO);
#if 0
printf("kVdd=%d\nvdd25=%d\nKvPTAT=%g\nKtPTAT=%g\nvPTAT25=%d\n", params->kVdd, params->vdd25, params->KvPTAT, params->KtPTAT, params->vPTAT25);
printf("alphaPTAT=%g\ngainEE=%d\ntgc=%g\ncpKv=%g\ncpKta=%g\n", params->alphaPTAT, params->gainEE, params->tgc, params->cpKv, params->cpKta);
printf("KsTa=%g\nCT[]={%g, %g, %g}\n", params->KsTa, params->CT[0], params->CT[1], params->CT[2]);
printf("KsTo[]={"); for(int i = 0; i < 4; ++i) printf("%s%g", (i) ? ", " : "", params->KsTo[i]); printf("}\n");
printf("alphacorr[]={"); for(int i = 0; i < 4; ++i) printf("%s%g", (i) ? ", " : "", params->alphacorr[i]); printf("}\n");
printf("alpha[]=\n"); dumpIma(params->alpha);
printf("offset[]=\n"); dumpIma(params->offset);
printf("kta[]=\n"); dumpIma(params->kta);
printf("kv[]={"); for(int i = 0; i < 4; ++i) printf("%s%g", (i) ? ", " : "", params->kv[i]); printf("}\n");
printf("cpAlpha[]={%g, %g}\n", params->cpAlpha[0], params->cpAlpha[1]);
printf("cpOffset[]={%d, %d}\n", params->cpOffset[0], params->cpOffset[1]);
printf("outliers[]=\n");
uint8_t *o = params->outliers;
for(int row = 0; row < MLX_H; ++row){
for(int col = 0; col < MLX_W; ++col){
printf("%d ", *o++);
}
printf("\n");
}
#endif
#undef CHKI
#undef CHKF
}
/*****************************************************************************
Calculate parameters & values
*****************************************************************************/
// fill OCC/ACC row/col arrays
static void occacc(int8_t *arr, int l, const uint16_t *regstart){
int n = l >> 2; // divide by 4
int8_t *p = arr;
for(int i = 0; i < n; ++i){
register uint16_t val = *regstart++;
*p++ = (val & 0x000F) >> 0;
*p++ = (val & 0x00F0) >> 4;
*p++ = (val & 0x0F00) >> 8;
*p++ = (val ) >> 12;
}
for(int i = 0; i < l; ++i, ++arr){
if(*arr > 0x07) *arr -= 0x10;
}
}
// get all parameters' values from `dataarray`, return FALSE if something failed
int get_parameters(const uint16_t dataarray[MLX_DMA_MAXLEN], MLX90640_params *params){
#define CREG_VAL(reg) dataarray[CREG_IDX(reg)]
int8_t i8;
int16_t i16;
uint16_t *pu16;
uint16_t val = CREG_VAL(REG_VDD);
i8 = (int8_t) (val >> 8);
params->kVdd = i8 * 32; // keep sign
if(params->kVdd == 0) return FALSE;
i16 = val & 0xFF;
params->vdd25 = ((i16 - 0x100) * 32) - (1<<13);
val = CREG_VAL(REG_KVTPTAT);
i16 = (val & 0xFC00) >> 10;
if(i16 > 0x1F) i16 -= 0x40;
params->KvPTAT = (fp_t)i16 / (1<<12);
i16 = (val & 0x03FF);
if(i16 > 0x1FF) i16 -= 0x400;
params->KtPTAT = (fp_t)i16 / 8.;
params->vPTAT25 = (int16_t) CREG_VAL(REG_PTAT);
val = CREG_VAL(REG_APTATOCCS) >> 12;
params->alphaPTAT = val / 4. + 8.;
params->gainEE = (int16_t)CREG_VAL(REG_GAIN);
if(params->gainEE == 0) return FALSE;
int8_t occRow[MLX_H];
int8_t occColumn[MLX_W];
occacc(occRow, MLX_H, &CREG_VAL(REG_OCCROW14));
occacc(occColumn, MLX_W, &CREG_VAL(REG_OCCCOL14));
int8_t accRow[MLX_H];
int8_t accColumn[MLX_W];
occacc(accRow, MLX_H, &CREG_VAL(REG_ACCROW14));
occacc(accColumn, MLX_W, &CREG_VAL(REG_ACCCOL14));
val = CREG_VAL(REG_APTATOCCS);
// need to do multiplication instead of bitshift, so:
fp_t occRemScale = 1<<(val&0x0F),
occColumnScale = 1<<((val>>4)&0x0F),
occRowScale = 1<<((val>>8)&0x0F);
int16_t offavg = (int16_t) CREG_VAL(REG_OSAVG);
// even/odd column/row numbers are for starting from 1, so for starting from 0 we should swap them:
// even - for 1,3,5,...; odd - for 0,2,4,... etc
int8_t ktaavg[4];
// 0 - odd row, odd col; 1 - odd row even col; 2 - even row, odd col; 3 - even row, even col
val = CREG_VAL(REG_KTAAVGODDCOL);
ktaavg[2] = (int8_t)(val & 0xFF); // odd col (1,3,..), even row (2,4,..) -> col 0,2,..; row 1,3,..
ktaavg[0] = (int8_t)(val >> 8); // odd col, odd row -> col 0,2,..; row 0,2,..
val = CREG_VAL(REG_KTAAVGEVENCOL);
ktaavg[3] = (int8_t)(val & 0xFF); // even col, even row -> col 1,3,..; row 1,3,..
ktaavg[1] = (int8_t)(val >> 8); // even col, odd row -> col 1,3,..; row 0,2,..
// so index of ktaavg is 2*(row&1)+(col&1)
val = CREG_VAL(REG_KTAVSCALE);
uint8_t scale1 = ((val & 0xFF)>>4) + 8, scale2 = (val&0xF);
if(scale1 == 0 || scale2 == 0) return FALSE;
fp_t mul = (fp_t)(1<<scale2), div = (fp_t)(1<<scale1); // kta_scales
uint16_t a_r = CREG_VAL(REG_SENSIVITY); // alpha_ref
val = CREG_VAL(REG_SCALEACC);
fp_t *a = params->alpha;
uint32_t diva32 = 1 << (val >> 12);
fp_t diva = (fp_t)(diva32);
diva *= (fp_t)(1<<30); // alpha_scale
DBG("diva: %g", diva);
fp_t accRowScale = 1<<((val & 0x0f00)>>8),
accColumnScale = 1<<((val & 0x00f0)>>4),
accRemScale = 1<<(val & 0x0f);
pu16 = (uint16_t*)&CREG_VAL(REG_OFFAK1);
fp_t *kta = params->kta, *offset = params->offset;
uint8_t *ol = params->outliers;
for(int row = 0; row < MLX_H; ++row){
int idx = (row&1)<<1;
for(int col = 0; col < MLX_W; ++col){
// offset
register uint16_t rv = *pu16++;
i16 = (rv & 0xFC00) >> 10;
if(i16 > 0x1F) i16 -= 0x40;
*offset++ = (fp_t)offavg + (fp_t)occRow[row]*occRowScale + (fp_t)occColumn[col]*occColumnScale + (fp_t)i16*occRemScale;
// kta
i16 = (rv & 0xF) >> 1;
if(i16 > 0x03) i16 -= 0x08;
*kta++ = (ktaavg[idx|(col&1)] + i16*mul) / div;
// alpha
i16 = (rv & 0x3F0) >> 4;
if(i16 > 0x1F) i16 -= 0x40;
fp_t oft = (fp_t)a_r + accRow[row]*accRowScale + accColumn[col]*accColumnScale +i16*accRemScale;
*a++ = oft / diva;
*ol++ = (rv&1) ? 1 : 0;
}
}
scale1 = (CREG_VAL(REG_KTAVSCALE) >> 8) & 0xF; // kvscale
div = (fp_t)(1<<scale1);
val = CREG_VAL(REG_KVAVG);
// kv indexes: +2 for odd (ÎÅÞÅÔÎÙÈ) rows, +1 for odd columns, so:
// [ 3, 2; 1, 0] for left upper corner (because datashit counts from 1, not from 0!)
i16 = val >> 12; if(i16 > 0x07) i16 -= 0x10;
ktaavg[0] = (int8_t)i16; // odd col, odd row
i16 = (val & 0xF0) >> 4; if(i16 > 0x07) i16 -= 0x10;
ktaavg[1] = (int8_t)i16; // even col, odd row
i16 = (val & 0x0F00) >> 8; if(i16 > 0x07) i16 -= 0x10;
ktaavg[2] = (int8_t)i16; // odd col, even row
i16 = val & 0x0F; if(i16 > 0x07) i16 -= 0x10;
ktaavg[3] = (int8_t)i16; // even col, even row
for(int i = 0; i < 4; ++i) params->kv[i] = ktaavg[i] / div;
val = CREG_VAL(REG_CPOFF);
params->cpOffset[0] = (val & 0x03ff);
if(params->cpOffset[0] > 0x1ff) params->cpOffset[0] -= 0x400;
params->cpOffset[1] = val >> 10;
if(params->cpOffset[1] > 0x1f) params->cpOffset[1] -= 0x40;
params->cpOffset[1] += params->cpOffset[0];
val = ((CREG_VAL(REG_KTAVSCALE) & 0xF0) >> 4) + 8;
i8 = (int8_t)(CREG_VAL(REG_KVTACP) & 0xFF);
params->cpKta = (fp_t)i8 / (1<<val);
val = (CREG_VAL(REG_KTAVSCALE) & 0x0F00) >> 8;
i16 = CREG_VAL(REG_KVTACP) >> 8;
if(i16 > 0x7F) i16 -= 0x100;
params->cpKv = (fp_t)i16 / (1<<val);
i16 = CREG_VAL(REG_KSTATGC) & 0xFF;
if(i16 > 0x7F) i16 -= 0x100;
params->tgc = (fp_t)i16;
params->tgc /= 32.;
val = (CREG_VAL(REG_SCALEACC)>>12); // alpha_scale_CP
i16 = CREG_VAL(REG_ALPHA)>>10; // cp_P1_P0_ratio
if(i16 > 0x1F) i16 -= 0x40;
div = (fp_t)(1<<val);
div *= (fp_t)(1<<27);
params->cpAlpha[0] = (fp_t)(CREG_VAL(REG_ALPHA) & 0x03FF) / div;
div = (fp_t)(1<<7);
params->cpAlpha[1] = params->cpAlpha[0] * (1. + (fp_t)i16/div);
i8 = (int8_t)(CREG_VAL(REG_KSTATGC) >> 8);
params->KsTa = (fp_t)i8/(1<<13);
div = 1<<((CREG_VAL(REG_CT34) & 0x0F) + 8); // kstoscale
DBG("kstoscale=%g (regct34=0x%04x)", div, CREG_VAL(REG_CT34));
val = CREG_VAL(REG_KSTO12);
DBG("ksto12=0x%04x", val);
i8 = (int8_t)(val & 0xFF);
DBG("To1ee=%d", i8);
params->KsTo[0] = i8 / div;
i8 = (int8_t)(val >> 8);
DBG("To2ee=%d", i8);
params->KsTo[1] = i8 / div;
val = CREG_VAL(REG_KSTO34);
DBG("ksto34=0x%04x", val);
i8 = (int8_t)(val & 0xFF);
DBG("To3ee=%d", i8);
params->KsTo[2] = i8 / div;
i8 = (int8_t)(val >> 8);
DBG("To4ee=%d", i8);
params->KsTo[3] = i8 / div;
// CT1 = -40, CT2 = 0 -> start from zero index, so CT[0] is CT2, CT[1] is CT3, CT[2] is CT4
params->CT[0] = 0.; // 0degr - between ranges 1 and 2
val = CREG_VAL(REG_CT34);
mul = ((val & 0x3000)>>12)*10.; // step
params->CT[1] = ((val & 0xF0)>>4)*mul; // CT3 - between ranges 2 and 3
params->CT[2] = ((val & 0x0F00) >> 8)*mul + params->CT[1]; // CT4 - between ranges 3 and 4
// alphacorr for each range: 11.1.11
params->alphacorr[0] = 1./(1. + params->KsTo[0] * 40.);
params->alphacorr[1] = 1.;
params->alphacorr[2] = (1. + params->KsTo[1] * params->CT[1]);
params->alphacorr[3] = (1. + params->KsTo[2] * (params->CT[2] - params->CT[1])) * params->alphacorr[2];
params->resolEE = (uint8_t)((CREG_VAL(REG_KTAVSCALE) & 0x3000) >> 12);
// Don't forget to check 'outlier' flags for wide purpose
return TRUE;
#undef CREG_VAL
}
/**
* @brief process_subpage - calculate all parameters from `dataarray` into `mlx_image`
* @param subpageno - number of subpage
* @param simpleimage == 0 - simplest, 1 - narrow range, 2 - extended range
*/
fp_t *process_subpage(MLX90640_params *params, const int16_t Frame[MLX_DMA_MAXLEN], int subpageno, int simpleimage){
#define IMD_VAL(reg) Frame[IMD_IDX(reg)]
DBG("\nprocess_subpage(%d)", subpageno);
#ifdef EBUG
chstate();
#endif
// 11.2.2.1. Resolution restore
// temporary:
fp_t resol_corr = (fp_t)(1<<params->resolEE) / (1<<2); // calibrated resol/current resol
DBG("resolEE=%d, resolCur=%d", params->resolEE, 2);
//fp_t resol_corr = (fp_t)(1<<params->resolEE) / (1<<((reg_control_val[subpageno]&0x0C00)>>10)); // calibrated resol/current resol
//DBG("resolEE=%d, resolCur=%d", params->resolEE, ((reg_control_val[subpageno]&0x0C00)>>10));
// 11.2.2.2. Supply voltage value calculation
int16_t i16a = (int16_t)IMD_VAL(REG_IVDDPIX);
fp_t dvdd = resol_corr*i16a - params->vdd25;
dvdd /= params->kVdd;
fp_t dV = i16a - params->vdd25; // for next step
dV /= params->kVdd;
DBG("ram=%d, vdd25=%d, dvdd=%g, resol=%g", i16a, params->vdd25, dvdd, resol_corr);
DBG("Vd=%g", dvdd+3.3);
// 11.2.2.3. Ambient temperature calculation
i16a = (int16_t)IMD_VAL(REG_ITAPTAT);
int16_t i16b = (int16_t)IMD_VAL(REG_ITAVBE);
fp_t dTa = (fp_t)i16a / (i16a * params->alphaPTAT + i16b); // vptatart
dTa *= (fp_t)(1<<18);
dTa = (dTa / (1. + params->KvPTAT*dV)) - params->vPTAT25;
dTa = dTa / params->KtPTAT; // without 25degr - Ta0
DBG("Ta=%g", dTa+25.);
// 11.2.2.4. Gain parameter calculation
i16a = (int16_t)IMD_VAL(REG_IGAIN);
fp_t Kgain = params->gainEE / (fp_t)i16a;
DBG("Kgain=%g", Kgain);
fp_t pixOS[2]; // pix_gain_CP_SPx
// 11.2.2.6.1
pixOS[0] = ((int16_t)IMD_VAL(REG_ICPSP0))*Kgain; // pix_OS_CP_SPx
pixOS[1] = ((int16_t)IMD_VAL(REG_ICPSP1))*Kgain;
DBG("pixGain: %g/%g", pixOS[0], pixOS[1]);
for(int i = 0; i < 2; ++i){ // calc pixOS by gain
// 11.2.2.6.2
pixOS[i] -= params->cpOffset[i]*(1. + params->cpKta*dTa)*(1. + params->cpKv*dvdd);
}
// now make first approximation to image
uint16_t pixno = 0; // current pixel number - for indexing in parameters etc
for(int row = 0, rowidx = 0; row < MLX_H; ++row, rowidx ^= 2){
for(int col = 0, idx = rowidx; col < MLX_W; ++col, ++pixno, idx ^= 1){
uint8_t sp = (row&1)^(col&1); // subpage of current pixel
if(sp != subpageno) continue;
// 11.2.2.5.1
fp_t curval = (fp_t)(Frame[pixno]) * Kgain; // gain compensation
// 11.2.2.5.3
curval -= params->offset[pixno] * (1. + params->kta[pixno]*dTa) *
(1. + params->kv[idx]*dvdd); // add offset
// now `curval` is pix_OS == V_IR_emiss_comp
// 11.2.2.7
fp_t IRcompens = curval - params->tgc * pixOS[subpageno]; // IR_compensated
if(simpleimage == 0){ // ???
curval = IRcompens;
/*
curval -= params->cpOffset[subpageno] * (1. - params->cpKta * dTa) *
(1. + params->cpKv * dvdd); // CP
curval = IRcompens - params->tgc * curval; // IR gradient compens
*/
}else{
// 11.2.2.8
fp_t alphaComp = params->alpha[pixno] - params->tgc * params->cpAlpha[subpageno];
alphaComp /= 1. + params->KsTa * dTa;
// 11.2.2.9: calculate To for basic range
fp_t Tar = dTa + 273.15 + 25.; // Ta+273.15
Tar = Tar*Tar*Tar*Tar; // T_aK4 (when \epsilon==1 this is T_{a-r} too)
fp_t ac3 = alphaComp*alphaComp*alphaComp;
fp_t Sx = ac3*IRcompens + alphaComp*ac3*Tar;
Sx = params->KsTo[1] * SQRT(SQRT(Sx));
fp_t To;
if(simpleimage == 1){
To = IRcompens / (alphaComp * (1. - 273.15*params->KsTo[1]) + Sx) + Tar;
}else{ // extended range
int idx = 0; // range 1 by default
fp_t ctx = -40.;
if(curval > params->CT[0] && curval < params->CT[1]){ // range 2
idx = 1; ctx = params->CT[0];
}else if(curval < params->CT[2]){ // range 3
idx = 2; ctx = params->CT[1];
}else{ // range 4
idx = 3; ctx = params->CT[2];
}
To = IRcompens / (alphaComp * params->alphacorr[idx] * (1. + params->KsTo[idx]*(curval - ctx))) + Tar;
}
curval = SQRT(SQRT(To)) - 273.15;
}
mlx_image[pixno] = curval;
}
}
DBG("Time: %g", sl_dtime()-Tlast);
return mlx_image;
#undef IMD_VAL
}
#if 0
// start image acquiring for next subpage
static int process_startima(int subpageno){
chstate();
DBG("startima(%d)", subpageno);
uint16_t reg, N;
while(1){
// write `overwrite` flag twice
if(!write_reg(REG_CONTROL, reg_control_val[subpageno]) ||
!write_reg(REG_STATUS, REG_STATUS_OVWEN) ||
!write_reg(REG_STATUS, REG_STATUS_OVWEN)) chkerr();
while(1){
if(read_reg(REG_STATUS, &reg)){
if(reg & REG_STATUS_NEWDATA){
DBG("got newdata: %g", sl_dtime() - Tlast);
if(subpageno != (reg & REG_STATUS_SPNO)){
DBG("wrong subpage number -> M_ERROR");
return FALSE;
}else{ // all OK, run image reading
chstate();
write_reg(REG_STATUS, 0); // clear rdy bit
N = MLX_PIXARRSZ;
if(read_data(REG_IMAGEDATA, &N) && N == MLX_PIXARRSZ){
DBG("got readoutm N=%d: %g", N, sl_dtime() - Tlast);
return TRUE;
}else chkerr();
}
}else chktmout();
}else chkerr();
}
}
return FALSE;
}
// if state of MLX allows, make an image else return error
// @param simple ==1 for simplest image processing (without T calibration)
int mlx90640_take_image(uint8_t simple, fp_t **image){
if(I2Cfd < 1) return FALSE;
if(params->kVdd == 0){ // no parameters -> make first run
if(!process_firstrun()) return FALSE;
}
DBG("\n\n\n-> M_STARTIMA");
for(int sp = 0; sp < 2; ++sp){
if(!process_startima(sp)) return FALSE; // get first subpage
process_subpage(sp, simple);
}
if(image) *image = mlx_image;
return TRUE;
}
#endif

66
MLX90640_test/mlx90640.h Normal file
View File

@ -0,0 +1,66 @@
/*
* This file is part of the mlxtest project.
* Copyright 2022 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
// floating type & sqrt operator
typedef double fp_t;
#define SQRT(x) sqrt((x))
// amount of pixels
#define MLX_W (32)
#define MLX_H (24)
#define MLX_PIXNO (MLX_W*MLX_H)
// pixels + service data
#define MLX_PIXARRSZ (MLX_PIXNO + 64)
typedef struct{
int16_t kVdd;
int16_t vdd25;
fp_t KvPTAT;
fp_t KtPTAT;
int16_t vPTAT25;
fp_t alphaPTAT;
int16_t gainEE;
fp_t tgc;
fp_t cpKv; // K_V_CP
fp_t cpKta; // K_Ta_CP
fp_t KsTa;
fp_t CT[3]; // range borders (0, 160, 320 degrC?)
fp_t KsTo[4]; // K_S_To for each range * 273.15
fp_t alphacorr[4]; // Alpha_corr for each range
fp_t alpha[MLX_PIXNO]; // full - with alpha_scale
fp_t offset[MLX_PIXNO];
fp_t kta[MLX_PIXNO]; // full K_ta - with scale1&2
fp_t kv[4]; // full - with scale; 0 - odd row, odd col; 1 - odd row even col; 2 - even row, odd col; 3 - even row, even col
fp_t cpAlpha[2]; // alpha_CP_subpage 0 and 1
uint8_t resolEE; // resolution_EE
int16_t cpOffset[2];
uint8_t outliers[MLX_PIXNO]; // outliers - bad pixels (if == 1)
} MLX90640_params;
// full amount of IMAGE data + EXTRA data (counts of uint16_t!)
#define MLX_DMA_MAXLEN (834)
int get_parameters(const uint16_t dataarray[MLX_DMA_MAXLEN], MLX90640_params *params);
void dump_parameters(MLX90640_params *params, const MLX90640_params *standard);
fp_t *process_subpage(MLX90640_params *params, const int16_t Frame[MLX_DMA_MAXLEN], int subpageno, int simpleimage);
void chkImage(const fp_t Image[MLX_PIXNO], const fp_t ToFrame[MLX_PIXNO]);
void dumpIma(const fp_t im[MLX_PIXNO]);

View File

@ -0,0 +1,90 @@
/*
* This file is part of the mlxtest project.
* Copyright 2022 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#define REG_STATUS 0x8000
#define REG_STATUS_OVWEN (1<<4)
#define REG_STATUS_NEWDATA (1<<3)
#define REG_STATUS_SPNO (1<<0)
#define REG_STATUS_SPMASK (3<<0)
#define REG_CONTROL 0x800D
#define REG_CONTROL_CHESS (1<<12)
#define REG_CONTROL_RES16 (0<<10)
#define REG_CONTROL_RES17 (1<<10)
#define REG_CONTROL_RES18 (2<<10)
#define REG_CONTROL_RES19 (3<<10)
#define REG_CONTROL_RESMASK (3<<10)
#define REG_CONTROL_REFR_05HZ (0<<7)
#define REG_CONTROL_REFR_1HZ (1<<7)
#define REG_CONTROL_REFR_2HZ (2<<7)
#define REG_CONTROL_REFR_4HZ (3<<7)
#define REG_CONTROL_REFR_8HZ (4<<7)
#define REG_CONTROL_REFR_16HZ (5<<7)
#define REG_CONTROL_REFR_32HZ (6<<7)
#define REG_CONTROL_REFR_64HZ (7<<7)
#define REG_CONTROL_SUBP1 (1<<4)
#define REG_CONTROL_SUBPMASK (3<<4)
#define REG_CONTROL_SUBPSEL (1<<3)
#define REG_CONTROL_DATAHOLD (1<<2)
#define REG_CONTROL_SUBPEN (1<<0)
// default value
#define REG_CONTROL_DEFAULT (REG_CONTROL_CHESS|REG_CONTROL_RES18|REG_CONTROL_REFR_2HZ|REG_CONTROL_SUBPEN)
// calibration data start & len
#define REG_CALIDATA 0x2400
#define REG_CALIDATA_LEN 832
#define REG_APTATOCCS 0x2410
#define REG_OSAVG 0x2411
#define REG_OCCROW14 0x2412
#define REG_OCCCOL14 0x2418
#define REG_SCALEACC 0x2420
#define REG_SENSIVITY 0x2421
#define REG_ACCROW14 0x2422
#define REG_ACCCOL14 0x2428
#define REG_GAIN 0x2430
#define REG_PTAT 0x2431
#define REG_KVTPTAT 0x2432
#define REG_VDD 0x2433
#define REG_KVAVG 0x2434
#define REG_ILCHESS 0x2435
#define REG_KTAAVGODDCOL 0x2436
#define REG_KTAAVGEVENCOL 0x2437
#define REG_KTAVSCALE 0x2438
#define REG_ALPHA 0x2439
#define REG_CPOFF 0x243A
#define REG_KVTACP 0x243B
#define REG_KSTATGC 0x243C
#define REG_KSTO12 0x243D
#define REG_KSTO34 0x243E
#define REG_CT34 0x243F
#define REG_OFFAK1 0x2440
// index of register in array (from REG_CALIDATA)
#define CREG_IDX(addr) ((addr)-REG_CALIDATA)
#define REG_IMAGEDATA 0x0400
#define REG_ITAVBE 0x0700
#define REG_ICPSP0 0x0708
#define REG_IGAIN 0x070A
#define REG_ITAPTAT 0x0720
#define REG_ICPSP1 0x0728
#define REG_IVDDPIX 0x072A
// index of register in array (from REG_IMAGEDATA)
#define IMD_IDX(addr) ((addr)-REG_IMAGEDATA)

View File

@ -0,0 +1 @@
-std=c17

View File

@ -0,0 +1,4 @@
#define GNU_SOURCE 1
#define _XOPEN_SOURCE 1111
#define EBUG

View File

@ -0,0 +1 @@
[General]

View File

@ -0,0 +1,218 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 17.0.1, 2025-09-09T23:05:00. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{7bd84e39-ca37-46d3-be9d-99ebea85bc0d}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="qlonglong">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoDetect">true</value>
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="qlonglong" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">KOI8-R</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.LineEndingBehavior">0</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="int" key="EditorConfiguration.PreferAfterWhitespaceComments">0</value>
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">false</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
<value type="bool" key="EditorConfiguration.tintMarginArea">true</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
<value type="bool" key="AutoTest.Framework.Boost">true</value>
<value type="bool" key="AutoTest.Framework.CTest">false</value>
<value type="bool" key="AutoTest.Framework.Catch">true</value>
<value type="bool" key="AutoTest.Framework.GTest">true</value>
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
</valuemap>
<value type="bool" key="AutoTest.ApplyFilter">false</value>
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
<valuelist type="QVariantList" key="AutoTest.PathFilters"/>
<value type="int" key="AutoTest.RunAfterBuild">0</value>
<value type="bool" key="AutoTest.UseGlobal">true</value>
<valuelist type="QVariantList" key="ClangCodeModel.CustomCommandLineKey"/>
<value type="bool" key="ClangCodeModel.UseGlobalConfig">true</value>
<value type="QString" key="ClangCodeModel.WarningConfigId">Builtin.BuildSystem</value>
<valuemap type="QVariantMap" key="ClangTools">
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
<value type="int" key="ClangTools.ParallelJobs">2</value>
<value type="bool" key="ClangTools.PreferConfigFile">false</value>
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="DeviceType">Desktop</value>
<value type="bool" key="HasPerBcDcs">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{65a14f9e-e008-4c1b-89df-4eaa4774b6e3}</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/tmp/1/home/eddy/MLX90640_wiringPi</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">По умолчанию</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="qlonglong">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>

View File

@ -0,0 +1 @@
-std=c++17

View File

@ -0,0 +1,5 @@
main.c
mlx90640.c
mlx90640.h
mlx90640_regs.h
testdata.h

View File

@ -0,0 +1,2 @@
/usr/local/include
.

1
MLX90640_test/offset.csv Normal file
View File

@ -0,0 +1 @@
-54,-56,-50,-60,-50,-56,-48,-60,-49,-57,-48,-61,-49,-59,-48,-63,-46,-59,-48,-65,-49,-61,-49,-66,-47,-64,-50,-69,-53,-66,-51,-75,-61,-64,-65,-61,-57,-63,-63,-61,-56,-63,-63,-62,-56,-65,-63,-64,-53,-65,-63,-65,-55,-66,-63,-66,-53,-69,-63,-69,-58,-70,-64,-74,-53,-56,-50,-60,-51,-55,-48,-60,-48,-56,-47,-61,-47,-58,-47,-62,-45,-58,-48,-64,-47,-59,-47,-67,-46,-62,-49,-68,-52,-65,-50,-75,-60,-62,-65,-61,-58,-61,-63,-61,-55,-62,-62,-61,-54,-64,-62,-62,-51,-64,-62,-63,-53,-66,-62,-66,-52,-67,-63,-67,-59,-71,-64,-75,-50,-54,-49,-58,-48,-54,-48,-59,-46,-55,-47,-60,-47,-55,-47,-61,-43,-57,-46,-62,-46,-59,-47,-65,-46,-61,-48,-67,-52,-65,-50,-74,-57,-63,-65,-60,-56,-62,-64,-61,-54,-63,-62,-62,-54,-62,-62,-63,-51,-64,-62,-63,-52,-66,-62,-65,-53,-69,-62,-69,-59,-71,-64,-75,-53,-54,-49,-59,-49,-54,-47,-58,-45,-54,-47,-60,-47,-55,-46,-62,-46,-57,-47,-62,-47,-58,-47,-64,-46,-61,-48,-66,-50,-64,-49,-73,-62,-64,-66,-62,-59,-63,-64,-61,-54,-63,-64,-62,-55,-63,-63,-63,-54,-65,-63,-64,-55,-65,-63,-65,-54,-68,-64,-68,-58,-72,-64,-75,-52,-53,-49,-58,-50,-53,-47,-58,-48,-54,-47,-60,-47,-55,-46,-61,-45,-57,-47,-63,-46,-58,-48,-65,-46,-61,-49,-67,-50,-64,-50,-73,-62,-65,-67,-63,-59,-63,-65,-63,-57,-64,-64,-63,-56,-65,-64,-64,-54,-66,-64,-65,-55,-68,-64,-66,-54,-69,-65,-69,-58,-72,-66,-75,-50,-54,-49,-58,-47,-53,-49,-59,-45,-55,-48,-60,-45,-55,-49,-62,-44,-57,-47,-63,-45,-58,-48,-65,-47,-62,-51,-69,-52,-65,-52,-75,-61,-66,-68,-64,-58,-65,-67,-64,-56,-66,-66,-65,-55,-66,-65,-65,-54,-67,-64,-66,-55,-68,-65,-68,-54,-71,-66,-71,-60,-73,-67,-77,-54,-54,-52,-59,-51,-54,-50,-59,-49,-55,-49,-60,-48,-55,-49,-62,-46,-58,-49,-65,-48,-60,-49,-66,-46,-63,-50,-68,-52,-64,-52,-75,-66,-69,-73,-66,-62,-68,-69,-65,-60,-67,-68,-65,-59,-67,-69,-66,-56,-69,-67,-68,-57,-69,-66,-69,-54,-71,-66,-70,-60,-73,-69,-77,-56,-54,-52,-59,-53,-54,-51,-60,-50,-56,-51,-61,-49,-56,-50,-63,-47,-58,-50,-65,-48,-59,-51,-66,-47,-62,-52,-68,-54,-66,-53,-76,-69,-71,-73,-69,-66,-69,-72,-68,-62,-69,-71,-67,-61,-69,-70,-68,-58,-70,-68,-70,-58,-70,-68,-69,-56,-71,-68,-71,-62,-74,-69,-79,-54,-55,-53,-60,-52,-55,-53,-61,-51,-57,-54,-63,-50,-57,-53,-64,-49,-59,-51,-65,-50,-60,-53,-66,-49,-63,-54,-71,-55,-66,-55,-76,-69,-73,-76,-70,-66,-71,-75,-69,-65,-72,-74,-71,-63,-71,-71,-71,-60,-71,-70,-70,-61,-71,-70,-70,-59,-74,-70,-75,-63,-76,-70,-80,-62,-57,-58,-62,-59,-57,-56,-62,-56,-59,-54,-64,-55,-60,-54,-65,-52,-61,-54,-67,-53,-62,-54,-68,-53,-64,-56,-71,-55,-67,-56,-75,-77,-77,-83,-74,-74,-74,-79,-73,-70,-74,-76,-73,-68,-74,-75,-73,-64,-75,-74,-73,-64,-74,-73,-74,-63,-76,-74,-75,-64,-78,-74,-80,-66,-58,-61,-65,-61,-59,-57,-64,-57,-60,-56,-66,-55,-60,-56,-67,-55,-63,-57,-69,-54,-63,-56,-69,-54,-64,-57,-72,-59,-67,-58,-77,-82,-80,-86,-78,-77,-78,-81,-76,-73,-78,-79,-76,-69,-76,-78,-75,-68,-78,-78,-76,-67,-76,-77,-76,-66,-78,-77,-77,-70,-80,-77,-84,-67,-60,-65,-66,-62,-61,-63,-68,-62,-62,-62,-67,-60,-64,-61,-70,-57,-65,-59,-69,-56,-65,-59,-71,-58,-67,-62,-74,-63,-69,-63,-78,-94,-91,-100,-88,-88,-88,-95,-87,-85,-87,-92,-86,-82,-88,-90,-88,-78,-87,-88,-84,-77,-87,-87,-86,-76,-89,-87,-90,-80,-90,-87,-93
1 -54 -56 -50 -60 -50 -56 -48 -60 -49 -57 -48 -61 -49 -59 -48 -63 -46 -59 -48 -65 -49 -61 -49 -66 -47 -64 -50 -69 -53 -66 -51 -75 -61 -64 -65 -61 -57 -63 -63 -61 -56 -63 -63 -62 -56 -65 -63 -64 -53 -65 -63 -65 -55 -66 -63 -66 -53 -69 -63 -69 -58 -70 -64 -74 -53 -56 -50 -60 -51 -55 -48 -60 -48 -56 -47 -61 -47 -58 -47 -62 -45 -58 -48 -64 -47 -59 -47 -67 -46 -62 -49 -68 -52 -65 -50 -75 -60 -62 -65 -61 -58 -61 -63 -61 -55 -62 -62 -61 -54 -64 -62 -62 -51 -64 -62 -63 -53 -66 -62 -66 -52 -67 -63 -67 -59 -71 -64 -75 -50 -54 -49 -58 -48 -54 -48 -59 -46 -55 -47 -60 -47 -55 -47 -61 -43 -57 -46 -62 -46 -59 -47 -65 -46 -61 -48 -67 -52 -65 -50 -74 -57 -63 -65 -60 -56 -62 -64 -61 -54 -63 -62 -62 -54 -62 -62 -63 -51 -64 -62 -63 -52 -66 -62 -65 -53 -69 -62 -69 -59 -71 -64 -75 -53 -54 -49 -59 -49 -54 -47 -58 -45 -54 -47 -60 -47 -55 -46 -62 -46 -57 -47 -62 -47 -58 -47 -64 -46 -61 -48 -66 -50 -64 -49 -73 -62 -64 -66 -62 -59 -63 -64 -61 -54 -63 -64 -62 -55 -63 -63 -63 -54 -65 -63 -64 -55 -65 -63 -65 -54 -68 -64 -68 -58 -72 -64 -75 -52 -53 -49 -58 -50 -53 -47 -58 -48 -54 -47 -60 -47 -55 -46 -61 -45 -57 -47 -63 -46 -58 -48 -65 -46 -61 -49 -67 -50 -64 -50 -73 -62 -65 -67 -63 -59 -63 -65 -63 -57 -64 -64 -63 -56 -65 -64 -64 -54 -66 -64 -65 -55 -68 -64 -66 -54 -69 -65 -69 -58 -72 -66 -75 -50 -54 -49 -58 -47 -53 -49 -59 -45 -55 -48 -60 -45 -55 -49 -62 -44 -57 -47 -63 -45 -58 -48 -65 -47 -62 -51 -69 -52 -65 -52 -75 -61 -66 -68 -64 -58 -65 -67 -64 -56 -66 -66 -65 -55 -66 -65 -65 -54 -67 -64 -66 -55 -68 -65 -68 -54 -71 -66 -71 -60 -73 -67 -77 -54 -54 -52 -59 -51 -54 -50 -59 -49 -55 -49 -60 -48 -55 -49 -62 -46 -58 -49 -65 -48 -60 -49 -66 -46 -63 -50 -68 -52 -64 -52 -75 -66 -69 -73 -66 -62 -68 -69 -65 -60 -67 -68 -65 -59 -67 -69 -66 -56 -69 -67 -68 -57 -69 -66 -69 -54 -71 -66 -70 -60 -73 -69 -77 -56 -54 -52 -59 -53 -54 -51 -60 -50 -56 -51 -61 -49 -56 -50 -63 -47 -58 -50 -65 -48 -59 -51 -66 -47 -62 -52 -68 -54 -66 -53 -76 -69 -71 -73 -69 -66 -69 -72 -68 -62 -69 -71 -67 -61 -69 -70 -68 -58 -70 -68 -70 -58 -70 -68 -69 -56 -71 -68 -71 -62 -74 -69 -79 -54 -55 -53 -60 -52 -55 -53 -61 -51 -57 -54 -63 -50 -57 -53 -64 -49 -59 -51 -65 -50 -60 -53 -66 -49 -63 -54 -71 -55 -66 -55 -76 -69 -73 -76 -70 -66 -71 -75 -69 -65 -72 -74 -71 -63 -71 -71 -71 -60 -71 -70 -70 -61 -71 -70 -70 -59 -74 -70 -75 -63 -76 -70 -80 -62 -57 -58 -62 -59 -57 -56 -62 -56 -59 -54 -64 -55 -60 -54 -65 -52 -61 -54 -67 -53 -62 -54 -68 -53 -64 -56 -71 -55 -67 -56 -75 -77 -77 -83 -74 -74 -74 -79 -73 -70 -74 -76 -73 -68 -74 -75 -73 -64 -75 -74 -73 -64 -74 -73 -74 -63 -76 -74 -75 -64 -78 -74 -80 -66 -58 -61 -65 -61 -59 -57 -64 -57 -60 -56 -66 -55 -60 -56 -67 -55 -63 -57 -69 -54 -63 -56 -69 -54 -64 -57 -72 -59 -67 -58 -77 -82 -80 -86 -78 -77 -78 -81 -76 -73 -78 -79 -76 -69 -76 -78 -75 -68 -78 -78 -76 -67 -76 -77 -76 -66 -78 -77 -77 -70 -80 -77 -84 -67 -60 -65 -66 -62 -61 -63 -68 -62 -62 -62 -67 -60 -64 -61 -70 -57 -65 -59 -69 -56 -65 -59 -71 -58 -67 -62 -74 -63 -69 -63 -78 -94 -91 -100 -88 -88 -88 -95 -87 -85 -87 -92 -86 -82 -88 -90 -88 -78 -87 -88 -84 -77 -87 -87 -86 -76 -89 -87 -90 -80 -90 -87 -93

81
MLX90640_test/testdata.h Normal file
View File

@ -0,0 +1,81 @@
/*
* This file is part of the mlxtest project.
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// data from MLX documentation for algorithms testing
// this file should be included only once!
#ifdef _TESTDATA_H__
#error "Don't include this file several times!"
#endif
#define _TESTDATA_H__
#include "mlx90640.h"
static const uint16_t EEPROM[MLX_DMA_MAXLEN] = {
#include "eeprom.csv"
};
static const int16_t FRAME0[MLX_DMA_MAXLEN] = {
#include "frame0.csv"
};
static const int16_t FRAME1[MLX_DMA_MAXLEN] = {
#include "frame1.csv"
};
static const MLX90640_params extracted_parameters = {
.kVdd = -3200
,.vdd25 = -12544
,.KvPTAT = 0.002197
,.KtPTAT = 42.625000
,.vPTAT25 = 12196
,.alphaPTAT = 9.0
,.gainEE = 5580
,.tgc = 0.000000
,.cpKv = 0.3750
,.cpKta = 0.004272
// ,.calibrationModeEE = 128
,.KsTa = -0.002441
,.CT = {0., 300., 500.}
,.KsTo = {-0.0002, -0.0002, -0.0002, -0.0002}
,.alphacorr = {}
,.alpha = {
#include "alpha.csv"
}
,.offset = {
#include "offset.csv"
}
,.kta = {
#include "kta.csv"
}
,.kv = {0.4375, 0.3750, 0.3750, 0.3750}
,.cpAlpha = {0.0000000028812792152, 0.0000000029037892091}
,.resolEE = 2
,.cpOffset = {-69, -65}
,.outliers = {0}
};
static const fp_t ToFrame[2][MLX_PIXNO] = {
{
#include "to_frame0.csv"
},
{
#include "to_frame0.csv"
}
};

View File

@ -0,0 +1 @@
28.711,0.000,28.178,0.000,28.313,0.000,28.631,0.000,28.295,0.000,28.586,0.000,28.336,0.000,28.683,0.000,28.248,0.000,28.573,0.000,29.127,0.000,28.629,0.000,29.070,0.000,28.894,0.000,28.883,0.000,29.687,0.000,0.000,28.618,0.000,28.134,0.000,28.517,0.000,27.982,0.000,28.092,0.000,27.914,0.000,28.156,0.000,28.261,0.000,28.198,0.000,28.528,0.000,28.837,0.000,28.730,0.000,28.814,0.000,28.558,0.000,28.605,0.000,29.005,28.513,0.000,28.482,0.000,28.428,0.000,27.946,0.000,28.248,0.000,28.387,0.000,28.340,0.000,28.128,0.000,28.544,0.000,28.321,0.000,28.986,0.000,28.445,0.000,28.728,0.000,28.778,0.000,29.035,0.000,28.639,0.000,0.000,28.116,0.000,28.237,0.000,28.173,0.000,28.519,0.000,28.303,0.000,28.049,0.000,28.246,0.000,28.466,0.000,28.247,0.000,28.378,0.000,28.835,0.000,28.480,0.000,28.828,0.000,28.428,0.000,28.528,0.000,29.030,28.163,0.000,28.719,0.000,28.268,0.000,28.439,0.000,28.607,0.000,28.199,0.000,28.440,0.000,28.730,0.000,28.361,0.000,28.256,0.000,28.253,0.000,28.563,0.000,28.593,0.000,28.962,0.000,28.746,0.000,28.917,0.000,0.000,28.112,0.000,27.955,0.000,28.365,0.000,28.291,0.000,28.335,0.000,28.610,0.000,28.281,0.000,28.303,0.000,28.246,0.000,28.710,0.000,28.723,0.000,28.532,0.000,28.493,0.000,28.686,0.000,28.565,0.000,28.197,28.489,0.000,28.295,0.000,28.610,0.000,28.497,0.000,29.183,0.000,29.365,0.000,28.763,0.000,28.320,0.000,28.594,0.000,28.641,0.000,28.629,0.000,28.950,0.000,27.980,0.000,28.349,0.000,28.453,0.000,28.852,0.000,0.000,28.364,0.000,28.244,0.000,28.023,0.000,28.247,0.000,29.572,0.000,30.034,0.000,28.667,0.000,28.642,0.000,28.316,0.000,28.311,0.000,28.444,0.000,28.168,0.000,28.403,0.000,28.438,0.000,28.800,0.000,27.913,28.091,0.000,28.598,0.000,28.313,0.000,28.813,0.000,30.533,0.000,32.362,0.000,31.328,0.000,29.314,0.000,28.126,0.000,28.517,0.000,28.644,0.000,28.679,0.000,28.485,0.000,28.714,0.000,28.549,0.000,28.739,0.000,0.000,28.275,0.000,27.955,0.000,28.448,0.000,28.965,0.000,31.879,0.000,32.094,0.000,31.286,0.000,29.814,0.000,28.593,0.000,28.303,0.000,28.808,0.000,28.596,0.000,28.363,0.000,28.585,0.000,28.420,0.000,28.341,28.595,0.000,28.668,0.000,28.724,0.000,29.526,0.000,32.332,0.000,32.878,0.000,32.587,0.000,32.080,0.000,30.319,0.000,29.259,0.000,28.347,0.000,28.371,0.000,28.591,0.000,28.779,0.000,28.650,0.000,28.577,0.000,0.000,28.330,0.000,28.480,0.000,28.753,0.000,30.412,0.000,33.225,0.000,33.175,0.000,32.848,0.000,31.967,0.000,30.751,0.000,29.090,0.000,28.461,0.000,28.636,0.000,28.437,0.000,28.707,0.000,28.479,0.000,28.493,28.450,0.000,28.205,0.000,28.945,0.000,30.841,0.000,33.628,0.000,34.155,0.000,33.454,0.000,32.621,0.000,32.219,0.000,31.181,0.000,28.945,0.000,28.535,0.000,28.354,0.000,28.338,0.000,28.487,0.000,28.645,0.000,0.000,28.420,0.000,28.235,0.000,29.189,0.000,32.258,0.000,34.054,0.000,34.386,0.000,33.366,0.000,32.502,0.000,32.534,0.000,30.553,0.000,28.703,0.000,28.123,0.000,28.465,0.000,28.435,0.000,28.666,0.000,28.458,28.412,0.000,28.618,0.000,29.437,0.000,32.515,0.000,33.633,0.000,34.348,0.000,33.460,0.000,33.297,0.000,32.441,0.000,30.583,0.000,28.901,0.000,28.751,0.000,28.455,0.000,28.551,0.000,28.674,0.000,28.816,0.000,0.000,28.379,0.000,28.929,0.000,30.135,0.000,32.781,0.000,33.364,0.000,33.703,0.000,33.035,0.000,32.681,0.000,32.508,0.000,29.271,0.000,28.813,0.000,28.176,0.000,28.281,0.000,28.308,0.000,28.441,0.000,28.851,28.266,0.000,28.759,0.000,30.103,0.000,32.329,0.000,32.560,0.000,32.963,0.000,32.587,0.000,32.781,0.000,32.408,0.000,29.431,0.000,28.536,0.000,28.849,0.000,28.187,0.000,28.597,0.000,29.227,0.000,28.880,0.000,0.000,28.259,0.000,28.992,0.000,30.442,0.000,32.244,0.000,32.250,0.000,32.312,0.000,32.854,0.000,32.680,0.000,31.065,0.000,28.621,0.000,28.344,0.000,28.447,0.000,28.667,0.000,28.698,0.000,29.718,0.000,28.963,28.429,0.000,28.570,0.000,29.163,0.000,29.828,0.000,31.671,0.000,32.379,0.000,32.265,0.000,32.592,0.000,31.175,0.000,29.218,0.000,28.587,0.000,28.508,0.000,28.957,0.000,29.226,0.000,31.199,0.000,31.547,0.000,0.000,28.137,0.000,28.463,0.000,28.557,0.000,29.704,0.000,30.460,0.000,31.603,0.000,31.990,0.000,32.004,0.000,29.626,0.000,28.473,0.000,28.220,0.000,28.694,0.000,28.703,0.000,29.831,0.000,32.548,0.000,32.176,28.473,0.000,28.264,0.000,28.662,0.000,28.372,0.000,29.225,0.000,29.797,0.000,30.999,0.000,31.244,0.000,29.575,0.000,28.510,0.000,28.530,0.000,28.449,0.000,28.642,0.000,30.602,0.000,32.418,0.000,34.239,0.000,0.000,28.322,0.000,28.078,0.000,28.183,0.000,28.517,0.000,28.593,0.000,29.005,0.000,30.394,0.000,30.257,0.000,28.746,0.000,28.295,0.000,28.561,0.000,28.250,0.000,28.593,0.000,31.333,0.000,33.254,0.000,33.224,28.516,0.000,28.317,0.000,28.465,0.000,28.845,0.000,28.642,0.000,28.464,0.000,28.858,0.000,28.979,0.000,29.039,0.000,28.832,0.000,28.280,0.000,28.837,0.000,29.056,0.000,31.946,0.000,33.101,0.000,32.988,0.000,0.000,28.161,0.000,28.257,0.000,28.346,0.000,28.301,0.000,28.101,0.000,28.288,0.000,28.615,0.000,28.926,0.000,28.512,0.000,28.178,0.000,28.392,0.000,28.412,0.000,30.166,0.000,32.614,0.000,32.530,0.000,32.028
1 28.711 0.000 28.178 0.000 28.313 0.000 28.631 0.000 28.295 0.000 28.586 0.000 28.336 0.000 28.683 0.000 28.248 0.000 28.573 0.000 29.127 0.000 28.629 0.000 29.070 0.000 28.894 0.000 28.883 0.000 29.687 0.000 0.000 28.618 0.000 28.134 0.000 28.517 0.000 27.982 0.000 28.092 0.000 27.914 0.000 28.156 0.000 28.261 0.000 28.198 0.000 28.528 0.000 28.837 0.000 28.730 0.000 28.814 0.000 28.558 0.000 28.605 0.000 29.005 28.513 0.000 28.482 0.000 28.428 0.000 27.946 0.000 28.248 0.000 28.387 0.000 28.340 0.000 28.128 0.000 28.544 0.000 28.321 0.000 28.986 0.000 28.445 0.000 28.728 0.000 28.778 0.000 29.035 0.000 28.639 0.000 0.000 28.116 0.000 28.237 0.000 28.173 0.000 28.519 0.000 28.303 0.000 28.049 0.000 28.246 0.000 28.466 0.000 28.247 0.000 28.378 0.000 28.835 0.000 28.480 0.000 28.828 0.000 28.428 0.000 28.528 0.000 29.030 28.163 0.000 28.719 0.000 28.268 0.000 28.439 0.000 28.607 0.000 28.199 0.000 28.440 0.000 28.730 0.000 28.361 0.000 28.256 0.000 28.253 0.000 28.563 0.000 28.593 0.000 28.962 0.000 28.746 0.000 28.917 0.000 0.000 28.112 0.000 27.955 0.000 28.365 0.000 28.291 0.000 28.335 0.000 28.610 0.000 28.281 0.000 28.303 0.000 28.246 0.000 28.710 0.000 28.723 0.000 28.532 0.000 28.493 0.000 28.686 0.000 28.565 0.000 28.197 28.489 0.000 28.295 0.000 28.610 0.000 28.497 0.000 29.183 0.000 29.365 0.000 28.763 0.000 28.320 0.000 28.594 0.000 28.641 0.000 28.629 0.000 28.950 0.000 27.980 0.000 28.349 0.000 28.453 0.000 28.852 0.000 0.000 28.364 0.000 28.244 0.000 28.023 0.000 28.247 0.000 29.572 0.000 30.034 0.000 28.667 0.000 28.642 0.000 28.316 0.000 28.311 0.000 28.444 0.000 28.168 0.000 28.403 0.000 28.438 0.000 28.800 0.000 27.913 28.091 0.000 28.598 0.000 28.313 0.000 28.813 0.000 30.533 0.000 32.362 0.000 31.328 0.000 29.314 0.000 28.126 0.000 28.517 0.000 28.644 0.000 28.679 0.000 28.485 0.000 28.714 0.000 28.549 0.000 28.739 0.000 0.000 28.275 0.000 27.955 0.000 28.448 0.000 28.965 0.000 31.879 0.000 32.094 0.000 31.286 0.000 29.814 0.000 28.593 0.000 28.303 0.000 28.808 0.000 28.596 0.000 28.363 0.000 28.585 0.000 28.420 0.000 28.341 28.595 0.000 28.668 0.000 28.724 0.000 29.526 0.000 32.332 0.000 32.878 0.000 32.587 0.000 32.080 0.000 30.319 0.000 29.259 0.000 28.347 0.000 28.371 0.000 28.591 0.000 28.779 0.000 28.650 0.000 28.577 0.000 0.000 28.330 0.000 28.480 0.000 28.753 0.000 30.412 0.000 33.225 0.000 33.175 0.000 32.848 0.000 31.967 0.000 30.751 0.000 29.090 0.000 28.461 0.000 28.636 0.000 28.437 0.000 28.707 0.000 28.479 0.000 28.493 28.450 0.000 28.205 0.000 28.945 0.000 30.841 0.000 33.628 0.000 34.155 0.000 33.454 0.000 32.621 0.000 32.219 0.000 31.181 0.000 28.945 0.000 28.535 0.000 28.354 0.000 28.338 0.000 28.487 0.000 28.645 0.000 0.000 28.420 0.000 28.235 0.000 29.189 0.000 32.258 0.000 34.054 0.000 34.386 0.000 33.366 0.000 32.502 0.000 32.534 0.000 30.553 0.000 28.703 0.000 28.123 0.000 28.465 0.000 28.435 0.000 28.666 0.000 28.458 28.412 0.000 28.618 0.000 29.437 0.000 32.515 0.000 33.633 0.000 34.348 0.000 33.460 0.000 33.297 0.000 32.441 0.000 30.583 0.000 28.901 0.000 28.751 0.000 28.455 0.000 28.551 0.000 28.674 0.000 28.816 0.000 0.000 28.379 0.000 28.929 0.000 30.135 0.000 32.781 0.000 33.364 0.000 33.703 0.000 33.035 0.000 32.681 0.000 32.508 0.000 29.271 0.000 28.813 0.000 28.176 0.000 28.281 0.000 28.308 0.000 28.441 0.000 28.851 28.266 0.000 28.759 0.000 30.103 0.000 32.329 0.000 32.560 0.000 32.963 0.000 32.587 0.000 32.781 0.000 32.408 0.000 29.431 0.000 28.536 0.000 28.849 0.000 28.187 0.000 28.597 0.000 29.227 0.000 28.880 0.000 0.000 28.259 0.000 28.992 0.000 30.442 0.000 32.244 0.000 32.250 0.000 32.312 0.000 32.854 0.000 32.680 0.000 31.065 0.000 28.621 0.000 28.344 0.000 28.447 0.000 28.667 0.000 28.698 0.000 29.718 0.000 28.963 28.429 0.000 28.570 0.000 29.163 0.000 29.828 0.000 31.671 0.000 32.379 0.000 32.265 0.000 32.592 0.000 31.175 0.000 29.218 0.000 28.587 0.000 28.508 0.000 28.957 0.000 29.226 0.000 31.199 0.000 31.547 0.000 0.000 28.137 0.000 28.463 0.000 28.557 0.000 29.704 0.000 30.460 0.000 31.603 0.000 31.990 0.000 32.004 0.000 29.626 0.000 28.473 0.000 28.220 0.000 28.694 0.000 28.703 0.000 29.831 0.000 32.548 0.000 32.176 28.473 0.000 28.264 0.000 28.662 0.000 28.372 0.000 29.225 0.000 29.797 0.000 30.999 0.000 31.244 0.000 29.575 0.000 28.510 0.000 28.530 0.000 28.449 0.000 28.642 0.000 30.602 0.000 32.418 0.000 34.239 0.000 0.000 28.322 0.000 28.078 0.000 28.183 0.000 28.517 0.000 28.593 0.000 29.005 0.000 30.394 0.000 30.257 0.000 28.746 0.000 28.295 0.000 28.561 0.000 28.250 0.000 28.593 0.000 31.333 0.000 33.254 0.000 33.224 28.516 0.000 28.317 0.000 28.465 0.000 28.845 0.000 28.642 0.000 28.464 0.000 28.858 0.000 28.979 0.000 29.039 0.000 28.832 0.000 28.280 0.000 28.837 0.000 29.056 0.000 31.946 0.000 33.101 0.000 32.988 0.000 0.000 28.161 0.000 28.257 0.000 28.346 0.000 28.301 0.000 28.101 0.000 28.288 0.000 28.615 0.000 28.926 0.000 28.512 0.000 28.178 0.000 28.392 0.000 28.412 0.000 30.166 0.000 32.614 0.000 32.530 0.000 32.028

File diff suppressed because one or more lines are too long