flir-gtk/cam-thread.c

709 lines
20 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (C) 2015-2016 Thomas <tomas123 @ EEVblog Electronics Community Forum>
* Copyright (C) 2021 Nicole Faerber <nicole.faerber@dpin.de>
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <gtk/gtk.h>
#include <cJSON.h>
// from main thread
void update_fb(void);
//extern unsigned char *fbdata;
extern gboolean flir_run;
extern unsigned char *color_palette;
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <libusb.h>
#include <time.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <assert.h>
#include <fcntl.h>
#include <math.h>
#include "cam-thread.h"
#include "planck.h"
// -----------------START-ORG-CODE------------------------------------------
#define VENDOR_ID 0x09cb
#define PRODUCT_ID 0x1996
static struct libusb_device_handle *devh = NULL;
//int filecount=0;
struct timeval t1, t2;
long long fps_t;
double t_min = 0.0;
double t_max = 0.0;
double t_center = 0.0;
int FFC = 0; // detect FFC
// -- buffer for EP 0x85 chunks ---------------
#define BUF85SIZE 1048576 // size got from android app
int buf85pointer = 0;
unsigned char buf85[BUF85SIZE];
extern unsigned char *jpeg_buffer;
extern unsigned int jpeg_size;
extern unsigned char *ir_buffer;
extern double emissivity;
extern double tempreflected;
struct shutter_state_t shutter_state;
struct battery_state_t battery_state;
// EP81 device status message (JSON)
// {
// "type":"batteryChargingCurrentUpdate",
// "data":
// {
// "chargingCurrent":0
// }
// }
// {
// "type":"batteryChargingStateUpdate",
// "data":
// {
// "chargingState":"stateNoCharging"
// }
// }
// {
// "type":"batteryVoltageUpdate",
// "data":
// {
// "voltage":3.76999998092651,
// "percentage":77
// }
// }
void
parse_status(unsigned char *buf)
{
cJSON *status_json = cJSON_Parse((char *)buf);
const cJSON *res = NULL;
if (status_json == NULL)
return;
res = cJSON_GetObjectItemCaseSensitive(status_json, "shutterState");
if (cJSON_IsString(res) && (res->valuestring != NULL)) {
if (strncmp(res->valuestring,"FFC",3)==0) {
shutter_state.shutterState=sFFC;
} else if (strncmp(res->valuestring,"ON",2)==0) {
shutter_state.shutterState=sON;
} else {
shutter_state.shutterState=sUNKNOWN;
}
}
res = cJSON_GetObjectItemCaseSensitive(status_json, "shutterTemperature");
if (cJSON_IsNumber(res)) {
shutter_state.shutterTemperature=res->valuedouble;
}
res = cJSON_GetObjectItemCaseSensitive(status_json, "usbNotifiedTimestamp");
if (cJSON_IsNumber(res)) {
shutter_state.usbNotifiedTimestamp=res->valuedouble;
}
res = cJSON_GetObjectItemCaseSensitive(status_json, "usbEnqueuedTimestamp");
if (cJSON_IsNumber(res)) {
shutter_state.usbEnqueuedTimestamp=res->valuedouble;
}
res = cJSON_GetObjectItemCaseSensitive(status_json, "ffcState");
if (cJSON_IsString(res) && (res->valuestring != NULL)) {
if (strncmp(res->valuestring,"FFC_VALID_RAD",13)==0) {
shutter_state.ffcState=FFC_VALID_RAD;
} else if (strncmp(res->valuestring,"FFC_PROGRESS",12)==0) {
shutter_state.ffcState=FFC_PROGRESS;
} else {
shutter_state.ffcState=FFC_UNKNOWN;
}
}
cJSON_Delete(status_json);
}
void
parse_config_in(unsigned char *buf)
{
cJSON *config_json = cJSON_Parse((char *)buf);
const cJSON *res = NULL;
const cJSON *res2 = NULL;
const cJSON *res3 = NULL;
if (config_json == NULL) {
fprintf(stderr, "config msg parse json failed\n");
return;
}// else
// fprintf(stderr, "config msg parse json\n%s\n", buf);
res = cJSON_GetObjectItemCaseSensitive(config_json, "type");
res2 = cJSON_GetObjectItemCaseSensitive(config_json, "data");
if (cJSON_IsString(res) && (res->valuestring != NULL)) {
if (strncmp(res->valuestring,"batteryVoltageUpdate",20)==0) {
res3 = cJSON_GetObjectItemCaseSensitive(res2, "voltage");
if (cJSON_IsNumber(res3)) {
battery_state.voltage = res3->valuedouble;
// printf("bat %.2fV\n", res3->valuedouble);
}
res3 = cJSON_GetObjectItemCaseSensitive(res2, "percentage");
if (cJSON_IsNumber(res3)) {
battery_state.percentage = res3->valueint;
// printf("bat %f%%\n", res3->valuedouble);
}
}
if (strncmp(res->valuestring,"batteryChargingCurrentUpdate",28)==0) {
res3 = cJSON_GetObjectItemCaseSensitive(res2, "chargingCurrent");
if (cJSON_IsNumber(res3)) {
battery_state.chargingCurrent = res3->valuedouble;
// printf("bat %.2fV\n", res3->valuedouble);
}
}
if (strncmp(res->valuestring,"batteryChargingStateUpdate",26)==0) {
res3 = cJSON_GetObjectItemCaseSensitive(res2, "chargingCurrent");
if (cJSON_IsString(res3) && (res3->valuestring != NULL)) {
printf("bat chg state '%s'\n", res3->valuestring);
}
}
}
cJSON_Delete(config_json);
}
double
raw2temperature(unsigned short RAW)
{
// mystery correction factor
RAW *=4;
// calc amount of radiance of reflected objects ( Emissivity < 1 )
double RAWrefl=PlanckR1/(PlanckR2*(exp(PlanckB/(tempreflected/*TempReflected*/+273.15))-PlanckF))-PlanckO;
// get displayed object temp max/min
double RAWobj=(RAW-(1-emissivity/*Emissivity*/)*RAWrefl)/emissivity/*Emissivity*/;
// calc object temperature
return PlanckB/log(PlanckR1/(PlanckR2*(RAWobj+PlanckO))+PlanckF)-273.15;
}
void vframe(char ep[],char EP_error[], int r, int actual_length, unsigned char buf[], unsigned char *colormap)
{
//static int fcnt=0;
// error handler
time_t now1;
now1 = time(NULL);
if (r < 0) {
if (strcmp (EP_error, libusb_error_name(r))!=0) {
strcpy(EP_error, libusb_error_name(r));
fprintf(stderr, "\n: %s >>>>>>>>>>>>>>>>>bulk transfer (in) %s:%i %s\n", ctime(&now1), ep , r, libusb_error_name(r));
sleep(1);
}
return;
}
// reset buffer if the new chunk begins with magic bytes or the buffer size limit is exceeded
unsigned char magicbyte[4]={0xEF,0xBE,0x00,0x00};
if ((strncmp ((char *)buf, (char *)magicbyte,4)==0 ) || ((buf85pointer + actual_length) >= BUF85SIZE)) {
//printf(">>>>>>>>>>>begin of new frame<<<<<<<<<<<<<\n");
buf85pointer=0;
}
// printf("actual_length %d !!!!!\n", actual_length);
memmove(buf85+buf85pointer, buf, actual_length);
buf85pointer=buf85pointer+actual_length;
if ((strncmp ((char *)buf85, (char *)magicbyte,4)!=0 )) {
//reset buff pointer
buf85pointer=0;
printf("Reset buffer because of bad Magic Byte!\n");
return;
}
// a quick and dirty job for gcc
uint32_t FrameSize = buf85[ 8] + (buf85[ 9] << 8) + (buf85[10] << 16) + (buf85[11] << 24);
uint32_t ThermalSize = buf85[12] + (buf85[13] << 8) + (buf85[14] << 16) + (buf85[15] << 24);
uint32_t JpgSize = buf85[16] + (buf85[17] << 8) + (buf85[18] << 16) + (buf85[19] << 24);
uint32_t StatusSize = buf85[20] + (buf85[21] << 8) + (buf85[22] << 16) + (buf85[23] << 24);
if ( (FrameSize+28) > (buf85pointer) ) {
// wait for next chunk
return;
}
if (StatusSize > 10) {
parse_status(&buf85[28+ThermalSize+JpgSize]);
}
int i,v;
// get a full frame, first print the status
t1=t2;
gettimeofday(&t2, NULL);
buf85pointer=0;
unsigned short pix[160*120]; // original Flir 16 Bit RAW
int x, y;
unsigned char *fb_proc,*fb_proc2;
fb_proc = malloc(160 * 128); // 8 Bit gray buffer really needs only 160 x 120
memset(fb_proc, 128, 160*128); // sizeof(fb_proc) doesn't work, value depends from LUT
fb_proc2 = malloc(160 * 128 * 3); // 8x8x8 Bit RGB buffer
int min = 0x10000, max = 0;
float rms = 0;
// Make a unsigned short array from what comes from the thermal frame
// find the max, min and RMS (not used yet) values of the array
//int maxx, maxy;
for (y = 0; y < 120; ++y) {
for (x = 0; x < 160; ++x) {
if (x<80)
v = buf85[2*(y * 164 + x) +32]+256*buf85[2*(y * 164 + x) +33];
else
v = buf85[2*(y * 164 + x) +32+4]+256*buf85[2*(y * 164 + x) +33+4];
pix[y * 160 + x] = v; // unsigned char!!
if (v < min)
min = v;
if (v > max) {
max = v; //maxx = x; maxy = y;
}
rms += v * v;
}
}
// RMS used later
// rms /= 160 * 120;
// rms = sqrtf(rms);
// scale the data in the array
int delta = max - min;
if (!delta)
delta = 1; // if max = min we have divide by zero
int scale = 0x10000 / delta;
for (y = 0; y < 120; ++y) { //120
for (x = 0; x < 160; ++x) { //160
int v = (pix[y * 160 + x] - min) * scale >> 8;
// fb_proc is the gray scale frame buffer
fb_proc[y * 160 + x] = v; // unsigned char!!
}
}
// calc medium of 2x2 center pixels
int med = (pix[59 * 160 + 79]+pix[59 * 160 + 80]+pix[60 * 160 + 79]+pix[60 * 160 + 80])/4;
t_min = raw2temperature(min);
t_max = raw2temperature(max);
t_center = raw2temperature(med);
if (ir_buffer == NULL) {
ir_buffer = (unsigned char *)malloc(640*480*4);
fprintf(stderr, "nb\n");
}
for (y = 0; y < 120; ++y) {
for (x = 0; x < 160; ++x) {
// fb_proc is the gray scale frame buffer
v=fb_proc[y * 160 + x] ; // unsigned char!!
ir_buffer[4*y * 160 + x*4] =
color_palette[3 * v + 2]; // B
ir_buffer[(4*y * 160 + x*4)+1] =
color_palette[3 * v + 1]; // G
ir_buffer[(4*y * 160 + x*4)+2] =
color_palette[3 * v]; // R
// ir_buffer[(4*y * 160 + x*4)+3] =
// 0x00; // A, empty
}
}
if (jpeg_size == 0 && JpgSize > 0) {
jpeg_size=JpgSize;
jpeg_buffer=(unsigned char *)malloc(jpeg_size);
memcpy(jpeg_buffer, &buf85[28+ThermalSize], jpeg_size);
}
if (strncmp ((char *)&buf85[28+ThermalSize+JpgSize+17],"FFC",3)==0) {
FFC=1; // drop all FFC frames
} else {
if (FFC==1) {
FFC=0; // drop first frame after FFC
} else {
// write(fdwr2, fb_proc2, framesize2); // colorized RGB Thermal Image
update_fb();
}
}
// free memory
free(fb_proc); // thermal RAW
free(fb_proc2); // visible jpg
}
static int
find_lvr_flirusb(void)
{
devh = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
return devh ? 0 : -EIO;
}
void
print_bulk_result(char ep[],char EP_error[], int r, int actual_length, unsigned char buf[])
{
time_t now1;
int i;
now1 = time(NULL);
if (r < 0) {
if (strcmp (EP_error, libusb_error_name(r))!=0) {
strcpy(EP_error, libusb_error_name(r));
fprintf(stderr, "\n: %s >>>>>>>>>>>>>>>>>bulk transfer (in) %s:%i %s\n", ctime(&now1), ep , r, libusb_error_name(r));
sleep(1);
}
//return 1;
} else {
printf("\n: %s bulk read EP %s, actual length %d\nHEX:\n",ctime(&now1), ep ,actual_length);
// write frame to file
/*
char filename[100];
sprintf(filename, "EP%s#%05i.bin",ep,filecount);
//filecount++;
FILE *file = fopen(filename, "wb");
fwrite(buf, 1, actual_length, file);
fclose(file);
*/
// hex print of first byte
#if 1
for (i = 0; i < (((200)<(actual_length))?(200):(actual_length)); i++) {
printf(" %02x", buf[i]);
}
#else
printf("\nSTRING:\n");
for (i = 0; i < (((200)<(actual_length))?(200):(actual_length)); i++) {
if (isascii(buf[i])) {
printf("%c", buf[i]);
}
}
#endif
printf("\n");
}
}
int
EPloop(unsigned char *colormap)
{
int r = 1;
r = libusb_init(NULL);
if (r < 0) {
fprintf(stderr, "failed to initialise libusb\n");
return(1);
}
r = find_lvr_flirusb();
if (r < 0) {
fprintf(stderr, "Could not find/open device\n");
goto out;
}
printf("Successfully found the Flir One G2 device\n");
r = libusb_set_configuration(devh, 3);
if (r < 0) {
fprintf(stderr, "libusb_set_configuration error %d\n", r);
goto out;
}
printf("Successfully set usb configuration 3\n");
// Claiming of interfaces is a purely logical operation;
// it does not cause any requests to be sent over the bus.
r = libusb_claim_interface(devh, 0);
if (r <0) {
fprintf(stderr, "libusb_claim_interface 0 error %d\n", r);
goto out;
}
r = libusb_claim_interface(devh, 1);
if (r < 0) {
fprintf(stderr, "libusb_claim_interface 1 error %d\n", r);
goto out;
}
r = libusb_claim_interface(devh, 2);
if (r < 0) {
fprintf(stderr, "libusb_claim_interface 2 error %d\n", r);
goto out;
}
printf("Successfully claimed interface 0,1,2\n");
unsigned char buf[1048576];
int actual_length;
time_t now;
// save last error status to avoid clutter the log
//char EP81_error[50]="", EP83_error[50]="",
char EP85_error[50]="";
unsigned char data[2]={0,0}; // only a bad dummy
// don't forget: $ sudo modprobe v4l2loopback video_nr=0,1
// startv4l2();
int state = 1;
// int ct=0;
while (flir_run) {
switch(state) {
case 1:
/* Flir config
01 0b 01 00 01 00 00 00 c4 d5
0 bmRequestType = 01
1 bRequest = 0b
2 wValue 0001 type (H) index (L) stop=0/start=1 (Alternate Setting)
4 wIndex 01 interface 1/2
5 wLength 00
6 Data 00 00
libusb_control_transfer (*dev_handle, bmRequestType, bRequest, wValue, wIndex, *data, wLength, timeout)
*/
printf("stop interface 2 FRAME\n");
r = libusb_control_transfer(devh,1,0x0b,0,2,data,0,100);
if (r < 0) {
fprintf(stderr, "Control Out error %d\n", r);
return r;
}
printf("stop interface 1 FILEIO\n");
r = libusb_control_transfer(devh,1,0x0b,0,1,data,0,100);
if (r < 0) {
fprintf(stderr, "Control Out error %d\n", r);
return r;
}
printf("\nstart interface 1 FILEIO\n");
r = libusb_control_transfer(devh,1,0x0b,1,1,data,0,100);
if (r < 0) {
fprintf(stderr, "Control Out error %d\n", r);
return r;
}
now = time(0); // Get the system time
printf("\n:xx %s",ctime(&now));
state = 3; // jump over wait stait 2. Not really using any data from CameraFiles.zip
break;
case 2:
printf("\nask for CameraFiles.zip on EP 0x83:\n");
now = time(0); // Get the system time
printf("\n: %s",ctime(&now));
int transferred = 0;
char my_string[128];
//--------- write string: {"type":"openFile","data":{"mode":"r","path":"CameraFiles.zip"}}
int length = 16;
unsigned char my_string2[16]={0xcc,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x41,0x00,0x00,0x00,0xF8,0xB3,0xF7,0x00};
printf("\nEP 0x02 to be sent Hexcode: %i Bytes[",length);
int i;
for (i = 0; i < length; i++) {
printf(" %02x", my_string2[i]);
}
printf(" ]\n");
r = libusb_bulk_transfer(devh, 2, my_string2, length, &transferred, 0);
if (r == 0 && transferred == length) {
printf("\nWrite successful!");
} else
printf("\nError in write! res = %d and transferred = %d\n", r, transferred);
strcpy( my_string,"{\"type\":\"openFile\",\"data\":{\"mode\":\"r\",\"path\":\"CameraFiles.zip\"}}");
length = strlen(my_string)+1;
printf("\nEP 0x02 to be sent: %s", my_string);
// avoid error: invalid conversion from char* to unsigned char* [-fpermissive]
unsigned char *my_string1 = (unsigned char*)my_string;
//my_string1 = (unsigned char*)my_string;
r = libusb_bulk_transfer(devh, 2, my_string1, length, &transferred, 0);
if (r == 0 && transferred == length) {
printf("\nWrite successful!");
printf("\nSent %d bytes with string: %s\n", transferred, my_string);
} else
printf("\nError in write! res = %d and transferred = %d\n", r, transferred);
//--------- write string: {"type":"readFile","data":{"streamIdentifier":10}}
length = 16;
unsigned char my_string3[16]={0xcc,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0xef,0xdb,0xc1,0xc1};
printf("\nEP 0x02 to be sent Hexcode: %i Bytes[",length);
for (i = 0; i < length; i++) {
printf(" %02x", my_string3[i]);
}
printf(" ]\n");
r = libusb_bulk_transfer(devh, 2, my_string3, length, &transferred, 0);
if(r == 0 && transferred == length) {
printf("\nWrite successful!");
} else
printf("\nError in write! res = %d and transferred = %d\n", r, transferred);
//strcpy( my_string, "{\"type\":\"setOption\",\"data\":{\"option\":\"autoFFC\",\"value\":true}}");
strcpy( my_string,"{\"type\":\"readFile\",\"data\":{\"streamIdentifier\":10}}");
length = strlen(my_string)+1;
printf("\nEP 0x02 to be sent %i Bytes: %s", length, my_string);
// avoid error: invalid conversion from char* to unsigned char* [-fpermissive]
my_string1 = (unsigned char*)my_string;
r = libusb_bulk_transfer(devh, 2, my_string1, length, &transferred, 0);
if (r == 0 && transferred == length) {
printf("\nWrite successful!");
printf("\nSent %d bytes with string: %s\n", transferred, my_string);
} else
printf("\nError in write! res = %d and transferred = %d\n", r, transferred);
// go to next state
now = time(0); // Get the system time
printf("\n: %s",ctime(&now));
//sleep(1);
state = 3;
break;
case 3:
printf("\nAsk for video stream, start EP 0x85:\n");
r = libusb_control_transfer(devh,1,0x0b,1,2,data, 2,200);
if (r < 0) {
fprintf(stderr, "Control Out error %d\n", r);
return r;
};
state = 4;
break;
case 4:
// endless loop
// poll Frame Endpoints 0x85
// don't change timeout=100ms !!
r = libusb_bulk_transfer(devh, 0x85, buf, sizeof(buf), &actual_length, 100);
if (actual_length > 0) {
// print_bulk_result("0x85", "none", r, actual_length, buf);
vframe("0x85",EP85_error, r, actual_length, buf, colormap);
}
break;
}
// poll Endpoints 0x81, 0x83
r = libusb_bulk_transfer(devh, 0x81, buf, sizeof(buf), &actual_length, 10);
if (actual_length > 16) {
int i;
unsigned int magic, second, len;
magic=*(unsigned int *)&buf[0];
second=*(unsigned int *)&buf[4];
len=*(unsigned int *)&buf[8];
fprintf(stderr, "EP81=%d in %d, magic=0x%08x sec=%08x len=%08x\n", r, actual_length,magic,second,len);
if (magic == 0x000001cc) {
parse_config_in(&buf[16]);
}
#if 0
for (i=0; i<actual_length; i++) {
if (buf[i] > 31 && buf[i]<128)
fprintf(stderr, "%c", buf[i]);
else
//fprintf(stderr, ".");
fprintf(stderr, "<%02x>", buf[i]);
}
fprintf(stderr, "\n");
#endif
}
/*
if (actual_length > 0 && actual_length <= 101) {
char k[5];
int i;
if (strncmp (&buf[32],"VoltageUpdate",13)==0) {
printf("xx %d\n",actual_length);
char *token, *string, *tofree, *string2;
// char l;
strcpy(string,buf);
// string = buf;
// assert(string != NULL);
printf("yy\n");
for (i = 32; i < (((200)<(actual_length))?(200):(actual_length)); i++) {
if (string[i]>31) {
printf("%c", string[i]);
// printf("%d ", i);
// string2[i-32] = string[i];
}
}
while ((token = strsep(&string, ":")) != NULL) {
printf("zz\n");
printf("%s\n", token);
}
// free(tofree);
// for (i = 32; i < (((200)<(actual_length))?(200):(actual_length)); i++) {
// if(buf[i]>31) {printf("%c", buf[i]);}
// }
}
}
*/
r = libusb_bulk_transfer(devh, 0x83, buf, sizeof(buf), &actual_length, 10);
if (strcmp(libusb_error_name(r), "LIBUSB_ERROR_NO_DEVICE")==0) {
fprintf(stderr, "EP 0x83 LIBUSB_ERROR_NO_DEVICE -> reset USB\n");
goto out;
}
if (actual_length > 0) {
//int i;
fprintf(stderr, "EP83 in %d\n", actual_length);
}
// print_bulk_result("0x83",EP83_error, r, actual_length, buf);
}
// never reached ;-)
libusb_release_interface(devh, 0);
out:
//close the device
if (devh != NULL) {
libusb_reset_device(devh);
libusb_close(devh);
libusb_exit(NULL);
}
return r >= 0 ? r : -r;
}
// -----------------END-ORG-CODE--------------------------------------------
gpointer cam_thread_main(gpointer user_data)
{
EPloop(NULL);
return NULL;
}