From ab5cbe7d6f3a98b941e76ebab6d74981268d9b8f Mon Sep 17 00:00:00 2001 From: Nicole Faerber Date: Mon, 1 Feb 2021 01:35:59 +0100 Subject: [PATCH] start parsing meta information from JSON objects coming through endpoints, like shutter status and battery status --- Makefile | 4 +- NOTES.txt | 128 +++++++++++++++++++++++++++++++ README.md | 15 ++++ cam-thread.c | 208 +++++++++++++++++++++++++++++++++++++++++++++------ cam-thread.h | 31 ++++++++ flirgtk.c | 190 +++++++++++++++++++++++----------------------- plank.h | 7 ++ 7 files changed, 461 insertions(+), 122 deletions(-) create mode 100644 cam-thread.h diff --git a/Makefile b/Makefile index 7b1cc95..36d31c5 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CC=gcc -CFLAGS=-g -O2 -Wall -D_REENTRANT `pkg-config --cflags gtk+-3.0 libusb-1.0 libjpeg` -LIBS=`pkg-config --libs gtk+-3.0 libusb-1.0 libjpeg` -lm +CFLAGS=-g -O2 -Wall -D_REENTRANT `pkg-config --cflags gtk+-3.0 libusb-1.0 libjpeg libcjson` +LIBS=`pkg-config --libs gtk+-3.0 libusb-1.0 libjpeg libcjson` -lm OBJ=flirgtk.o cam-thread.o cairo_jpg/src/cairo_jpg.o PRG=flirgtk diff --git a/NOTES.txt b/NOTES.txt index 1fda864..e0f6eaa 100644 --- a/NOTES.txt +++ b/NOTES.txt @@ -28,3 +28,131 @@ unsigned char * cairo_image_surface_get_data (cairo_surface_t *surfac get pointer to imag data for inspection _and_ manipulation + + + + +All messages are preceded by a header. +All headers are comprised of 32-bit words in little-endian order. +Headers of config endpoints have 4 words, those of file endpoints have 6 words, and those of frame endpoints are 7 words. +The first word is always the magic number (0x1cc for config, 0x5510 for file, 0xbeef for frame). +The second word appears to be always one. I'm still not sure about its meaning. +The third word is always the payload (message) size in bytes. +The last word is always the CRC-32 of all but the last word of header with the following parameters (this is the conventional CRC-32): +Polynomial: 0x04C11DB7, Init: 0xFFFFFFFF, Reflect Input: true, Reflect Output: true, XOR Output: 0xFFFFFFFF + +For file headers: +The fourth word is the stream identifier. +The fifth word is the conventional CRC-32 of the file itself. + +For frame headers: +The fourth word is the size of thermal image. +The fifth word is the size of visual (jpeg) image. +The sixth word is the size of status string. + +After issuing a command to the config endpoint, you can query it for a response. +For commands of type 'setOption', the response is of type 'setOptionStatus' and indicates the new value of the option (-1 if the option does not exist). +For commands of type 'openFile', the response is of type 'openFileStatus' and indicates the stream identifier of the opened file. The stream identifier is essentially the return value of the fopen function. Negative values indicate different error codes. Non-negative values could be used by a readFile command to read the opened file. +Analysis of the sdk binary reveals that there are also 'reboot' and 'upgradeFirmware' commands with corresponding responses 'rebootStatus' and 'upgradeFirmwareStatus'. But I haven't worked out the required arguments for these commands. + + +Meta data comes as JSON structures: + +EP81 +---- + +{ + "type":"sledInformation", + "data": + { + "serialNumberBoard":"F07H8J0055D", + "partNumberBoard":"invalid", + "versionBoard":"invalid", + "serialNumberLepton":"2171207", + "versionLepton":"3.3.26", + "leptonQR":"invalid", + "versionRosebudFactoryESW":"1.0.25", + "versionRosebudOperationalESW":"1.0.27", + "versionRosebudUpdaterESW":"1.0.25", + "versionRosebudAPI":"master.bc654fc", + "gitRevision":"master.bc654fc", + "automaticShutter":"Y", + "formFactor":"dongle", + "thermalHeight":"120", + "thermalWidth":"160", + "bigEndianThermal":"0", + "operatingMode":"operational" + } +} + +{ + "type":"batteryVoltageUpdate", + "data": + { + "voltage":3.90000009536743, + "percentage":77 + } +} + +{ + "type":"batteryChargingCurrentUpdate", + "data": + { + "chargingCurrent":0 + } +} + +{ + "type":"batteryChargingStateUpdate", + "data": + { + "chargingState":"stateNoCharging" + } +} +{ + "type":"batteryChargingStateUpdate", + "data": + { + "chargingState":"stateChargingSmartPhone" + } +} + + +EP81=0 in 101 +<01><00><00><01><00><00><00>U<00><00><00>0s<8c>{"type":"batteryVoltageUpdate","data":{"voltage":4.11999988555908,"percentage":100}}<00> +EP81=0 in 100 +<01><00><00><01><00><00><00>T<00><00><00>U<14>0g{"type":"batteryVoltageUpdate","data":{"voltage":4.1100001335144,"percentage":100}}<00> +EP81=0 in 101 +<01><00><00><01><00><00><00>U<00><00><00>0s<8c>{"type":"batteryVoltageUpdate","data":{"voltage":4.11999988555908,"percentage":100}}<00> +EP81=0 in 100 +<01><00><00><01><00><00><00>T<00><00><00>U<14>0g{"type":"batteryVoltageUpdate","data":{"voltage":4.1100001335144,"percentage":100}}<00> +EP81=0 in 101 +<01><00><00><01><00><00><00>U<00><00><00>0s<8c>{"type":"batteryVoltageUpdate","data":{"voltage":4.11999988555908,"percentage":100}}<00> +EP81=0 in 100 +<01><00><00><01><00><00><00>T<00><00><00>U<14>0g{"type":"batteryVoltageUpdate","data":{"voltage":4.1100001335144,"percentage":100}}<00> +EP81=0 in 101 +<01><00><00><01><00><00><00>U<00><00><00>0s<8c>{"type":"batteryVoltageUpdate","data":{"voltage":4.11999988555908,"percentage":100}}<00> + +There can be more than one message in one EP transfer: +EP81=0 in 294, magic=0x000001cc sec=00000001 len=00000055 +<01><00><00><01><00><00><00>U<00><00><00>0s<8c>{"type":"batteryVoltageUpdate","data":{"voltage":4.13000011444092,"percentage":100}}<00><01><00><00><01><00><00><00>H<00><00><00>r}{"type":"batteryChargingCurrentUpdate","data":{"chargingCurrent":1000}}<00><01><00><00><01><00><00><00>Y<00><00><00><88>Z<95>{"type":"batteryChargingStateUpdate","data":{"chargingState":"stateChargingSmartPhone"}}<00> + + + +EP85 +---- +{ + "shutterState":"FFC", + "shutterTemperature":309.239990234375, + "usbNotifiedTimestamp":1184541462.87291, + "usbEnqueuedTimestamp":1184541462.87494, + "ffcState":"FFC_PROGRESS" +} + +{ + "shutterState":"ON", + "shutterTemperature":310.679992675781, + "usbNotifiedTimestamp":1184542349.84666, + "usbEnqueuedTimestamp":1184542349.85135, + "ffcState":"FFC_VALID_RAD" +} diff --git a/README.md b/README.md index 81e5591..d47a9b9 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,21 @@ GTK+ application for FLIR ONE USB thermal camera based on flir-v4l: https://github.com/fnoop/flirone-v4l2 +== depdendencies == +GTK+-3.0 +Cairo +libusb-1.0 +libjpeg +libcjson +This should install everything under Debian and derivatives: + +apt install libgtk-3-dev libjpeg-dev libusb-1.0-0-dev libcjson-dev + +== building == +Makefile relies on pkg-config, if setup correctly simply running 'make' +should build the application. + +== libusb & udev == cp 77-flirone-lusb.rules /lib/udev/rules.d/ udevadm control --reload-rules diff --git a/cam-thread.c b/cam-thread.c index e1b940f..328b150 100644 --- a/cam-thread.c +++ b/cam-thread.c @@ -18,6 +18,7 @@ */ #include +#include // from main thread void update_fb(void); @@ -28,6 +29,7 @@ extern unsigned char *color_palette; #include +#include #include #include #include @@ -41,19 +43,17 @@ extern unsigned char *color_palette; #include #include +#include "cam-thread.h" #include "plank.h" // -----------------START-ORG-CODE------------------------------------------ - -// #include "jpeglib.h" - #define VENDOR_ID 0x09cb #define PRODUCT_ID 0x1996 static struct libusb_device_handle *devh = NULL; -int filecount=0; +//int filecount=0; struct timeval t1, t2; long long fps_t; @@ -73,6 +73,125 @@ extern unsigned char *jpeg_buffer; extern unsigned int jpeg_size; extern unsigned char *ir_buffer; +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) @@ -128,7 +247,7 @@ void vframe(char ep[],char EP_error[], int r, int actual_length, unsigned char b 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); + uint32_t StatusSize = buf85[20] + (buf85[21] << 8) + (buf85[22] << 16) + (buf85[23] << 24); //printf("FrameSize= %d (+28=%d), ThermalSize %d, JPG %d, StatusSize %d, Pointer %d\n",FrameSize,FrameSize+28, ThermalSize, JpgSize,StatusSize,buf85pointer); @@ -136,22 +255,31 @@ void vframe(char ep[],char EP_error[], int r, int actual_length, unsigned char b // wait for next chunk return; } - - int v; + + 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); // fps as moving average over last 20 frames // fps_t = (19*fps_t+10000000/(((t2.tv_sec * 1000000) + t2.tv_usec) - ((t1.tv_sec * 1000000) + t1.tv_usec)))/20; - filecount++; + //filecount++; // printf("#%08i %lld/10 fps:",filecount,fps_t); - // for (i = 0; i < StatusSize; i++) { - // v=28+ThermalSize+JpgSize+i; - // if(buf85[v]>31) {printf("%c", buf85[v]);} - // } - // printf("\n"); - +#if 0 + for (i = 0; i < StatusSize; i++) { + v=28+ThermalSize+JpgSize+i; + if (buf85[v]>31) { + printf("%c", buf85[v]); + } else { + printf("<%02x>", buf85[v]); + } + } + printf("\n"); +#endif buf85pointer=0; unsigned short pix[160*120]; // original Flir 16 Bit RAW @@ -273,8 +401,8 @@ void vframe(char ep[],char EP_error[], int r, int actual_length, unsigned char b 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 +// ir_buffer[(4*y * 160 + x*4)+3] = +// 0x00; // A, empty #if 0 // assemble one 32 bit pixel fbdata[16*y * 640 + x*16] = color_palette[3 * v + 2]; // B @@ -389,22 +517,24 @@ int i; /* char filename[100]; sprintf(filename, "EP%s#%05i.bin",ep,filecount); - 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 (buf[i]>31) { + if (isascii(buf[i])) { printf("%c", buf[i]); } } +#endif printf("\n"); } } @@ -506,7 +636,7 @@ int r = 1; } 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 + 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"); @@ -599,16 +729,42 @@ int r = 1; // 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) + 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); - /* + 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 1 + for (i=0; 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; @@ -636,13 +792,17 @@ int r = 1; // } } } - */ +*/ 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); } diff --git a/cam-thread.h b/cam-thread.h new file mode 100644 index 0000000..0b92362 --- /dev/null +++ b/cam-thread.h @@ -0,0 +1,31 @@ +#ifndef _CAM_THREAD_H +#define _CAM_THREAD_H + +enum ffcstate_t {FFC_VALID_RAD, FFC_PROGRESS, FFC_UNKNOWN}; +enum shutterstate_t {sON, sFFC, sUNKNOWN}; + +// { +// "shutterState":"ON", +// "shutterTemperature":310.679992675781, +// "usbNotifiedTimestamp":1184542349.84666, +// "usbEnqueuedTimestamp":1184542349.85135, +// "ffcState":"FFC_VALID_RAD" +// } +struct shutter_state_t { + enum shutterstate_t shutterState; // ON or FFC + double shutterTemperature; // in Kelvin? C = Kelvin - 273.15 + double usbNotifiedTimestamp; + double usbEnqueuedTimestamp; + enum ffcstate_t ffcState; // FFC_VALID_RAD or FFC_PROGRESS +}; + +enum chargingState_t {stateNoCharging, stateCharging, stateUNKNOWN}; + +struct battery_state_t { + enum chargingState_t chargingState; + double voltage; + int percentage; + double chargingCurrent; +}; + +#endif diff --git a/flirgtk.c b/flirgtk.c index 85dab88..9125ed0 100644 --- a/flirgtk.c +++ b/flirgtk.c @@ -25,6 +25,8 @@ #include // #include +#include "cam-thread.h" + #include "cairo_jpg/src/cairo_jpg.h" #include "palettes/15.h" @@ -60,6 +62,9 @@ unsigned char *color_palette; gpointer cam_thread_main(gpointer user_data); extern double t_min, t_max, t_center; +extern struct shutter_state_t shutter_state; +extern struct battery_state_t battery_state; + unsigned char *ir_buffer=NULL; unsigned char *jpeg_buffer=NULL; unsigned int jpeg_size=0; @@ -97,27 +102,28 @@ cairo_surface_t unsigned int *p1, *pc; int x,y; static cairo_surface_t *ps=NULL; -//static unsigned char *cm=NULL; cairo_t *cr; unsigned char *fbdata; char tdisp[16]; #define P_XPOS 175 +#define P_YPOS 2 +#define P_HEIGHT 14 if (ps==NULL) ps=cairo_image_surface_create(CAIRO_FORMAT_RGB24, 640, 20); cr=cairo_create(ps); fbdata=cairo_image_surface_get_data(ps); memset(fbdata,0,(640*20*4)); - y=5; + y=P_YPOS; for (x=0; x<256; x++) { fbdata[4* y * 640 + ((x+P_XPOS)*4)] = color_palette[3 * x + 2]; // B fbdata[(4* y * 640 + ((x+P_XPOS)*4))+1] = color_palette[3 * x + 1]; // G fbdata[(4* y * 640 + ((x+P_XPOS)*4))+2] = color_palette[3 * x]; // R } - y=5; + y=P_YPOS; p1 = (unsigned int *)&fbdata[4 * y * 640 + (P_XPOS*4)]; // pointer to start of line - for (y=5; y<15; y++) { + for (y=P_YPOS; y<(P_YPOS+P_HEIGHT); y++) { pc = (unsigned int *)&fbdata[4 * y * 640 + (P_XPOS*4)]; // pointer to start of copy line memcpy(pc,p1,256*4); } @@ -257,6 +263,15 @@ cairo_t *cr; cairo_move_to (cr, 330, 220); cairo_show_text (cr, tdisp); + // print battery % top right + snprintf(tdisp, 16, "%d%%", battery_state.percentage); + cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); + cairo_select_font_face (cr, "Sans", + CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size (cr, 14); + cairo_move_to (cr, 580, 20); + cairo_show_text (cr, tdisp); + cairo_destroy(cr); pending = FALSE; } @@ -292,7 +307,7 @@ char fname[30]; strftime (fname, 30, "ircam-%y%m%d%H%M%S", loctime); strncpy(pname, "./", PATH_MAX-30-4); // leave room for filename+extension strncat(pname, fname, PATH_MAX-5); // -5 to leave space for trailing \0 byte + extension - strncat(pname, ".png", PATH_MAX-1); // -5 to leave space for trailing \0 byte + extension + strncat(pname, ".png", PATH_MAX-1); // -1 to leave space for trailing \0 byte cairo_surface_write_to_png (psurface, pname); take_vis_shot=TRUE; @@ -303,6 +318,8 @@ start_clicked(GtkWidget *button, gpointer user_data) { if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(play_button))) { flir_run = TRUE; + memset(&shutter_state, 0, sizeof(shutter_state)); + memset(&battery_state, 0, sizeof(battery_state)); if (ir_buffer == NULL) g_printerr("ir_buffer\n"); g_thread_new ("CAM thread", cam_thread_main, NULL); @@ -339,14 +356,6 @@ close_window (void) gtk_main_quit(); } -gboolean -handle_timeout (gpointer user_data) -{ - update_fb(); - - return TRUE; -} - void palette_changed (GtkComboBox *widget, gpointer user_data) { @@ -381,108 +390,97 @@ GtkWidget *w, *i; // GtkWidget *da; - // default color palette + // init default color palette color_palette = palette_Rainbow; - if (!window) { // gappw=gtk_application_window_new(gapp); - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - gtk_window_set_title (GTK_WINDOW (window), "FLIR One"); + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (window), "FLIR One"); - g_signal_connect (window, "destroy", - G_CALLBACK (close_window), NULL); + g_signal_connect (window, "destroy", + G_CALLBACK (close_window), NULL); - box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4); - gtk_container_add (GTK_CONTAINER (window), box); + box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4); + gtk_container_add (GTK_CONTAINER (window), box); - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4); - gtk_container_add (GTK_CONTAINER (box), hbox); + hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4); + gtk_container_add (GTK_CONTAINER (box), hbox); - // 48 GTK_ICON_SIZE_DIALOG - // 32 GTK_ICON_SIZE_DND - // media-playback-start - // w = gtk_button_new_with_label("Start"); - // w = gtk_button_new_from_icon_name("media-playback-start", GTK_ICON_SIZE_DND); - play_button = gtk_toggle_button_new(); - i = gtk_image_new_from_icon_name("media-playback-start", GTK_ICON_SIZE_DND); - gtk_button_set_image(GTK_BUTTON(play_button),i); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(play_button), FALSE); - gtk_container_add (GTK_CONTAINER (hbox), play_button); + // 48 GTK_ICON_SIZE_DIALOG + // 32 GTK_ICON_SIZE_DND + // media-playback-start + // w = gtk_button_new_with_label("Start"); + // w = gtk_button_new_from_icon_name("media-playback-start", GTK_ICON_SIZE_DND); + play_button = gtk_toggle_button_new(); + i = gtk_image_new_from_icon_name("media-playback-start", GTK_ICON_SIZE_DND); + gtk_button_set_image(GTK_BUTTON(play_button),i); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(play_button), FALSE); + gtk_container_add (GTK_CONTAINER (hbox), play_button); + g_signal_connect (play_button, "clicked", + G_CALLBACK (start_clicked), NULL); - g_signal_connect (play_button, "clicked", - G_CALLBACK (start_clicked), NULL); + // media-playback-stop + // w = gtk_button_new_with_label("Stop"); + //w = gtk_button_new_from_icon_name("media-playback-stop", GTK_ICON_SIZE_DND); + stop_button = gtk_toggle_button_new(); + i = gtk_image_new_from_icon_name("media-playback-stop", GTK_ICON_SIZE_DND); + gtk_button_set_image(GTK_BUTTON(stop_button),i); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(stop_button), TRUE); + gtk_container_add (GTK_CONTAINER (hbox), stop_button); - // media-playback-stop - // w = gtk_button_new_with_label("Stop"); - //w = gtk_button_new_from_icon_name("media-playback-stop", GTK_ICON_SIZE_DND); - stop_button = gtk_toggle_button_new(); - i = gtk_image_new_from_icon_name("media-playback-stop", GTK_ICON_SIZE_DND); - gtk_button_set_image(GTK_BUTTON(stop_button),i); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(stop_button), TRUE); - gtk_container_add (GTK_CONTAINER (hbox), stop_button); + g_signal_connect (stop_button, "clicked", + G_CALLBACK (stop_clicked), NULL); - g_signal_connect (stop_button, "clicked", - G_CALLBACK (stop_clicked), NULL); + // drop down for color palettes + w = gtk_combo_box_text_new(); + gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT(w), NULL, "7"); + gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT(w), NULL, "15"); + gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT(w), NULL, "17"); + gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT(w), NULL, "85"); + gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT(w), NULL, "92"); + gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT(w), NULL, "Grayscale"); + gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT(w), NULL, "Grey"); + gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT(w), NULL, "Iron 2"); + gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT(w), NULL, "Iron Black"); + gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT(w), NULL, "Rainbow"); + gtk_combo_box_set_active (GTK_COMBO_BOX(w), 9); + gtk_container_add (GTK_CONTAINER (hbox), w); + g_signal_connect (w, "changed", + G_CALLBACK (palette_changed), NULL); - // drop down for color palettes - w = gtk_combo_box_text_new(); - gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT(w), NULL, "7"); - gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT(w), NULL, "15"); - gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT(w), NULL, "17"); - gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT(w), NULL, "85"); - gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT(w), NULL, "92"); - gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT(w), NULL, "Grayscale"); - gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT(w), NULL, "Grey"); - gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT(w), NULL, "Iron 2"); - gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT(w), NULL, "Iron Black"); - gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT(w), NULL, "Rainbow"); - gtk_combo_box_set_active (GTK_COMBO_BOX(w), 9); - gtk_container_add (GTK_CONTAINER (hbox), w); - g_signal_connect (w, "changed", - G_CALLBACK (palette_changed), NULL); + w = gtk_toggle_button_new_with_label("IR"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(w), TRUE); + gtk_container_add (GTK_CONTAINER (hbox), w); + g_signal_connect (w, "clicked", + G_CALLBACK (ircam_clicked), NULL); - w = gtk_toggle_button_new_with_label("IR"); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(w), TRUE); - gtk_container_add (GTK_CONTAINER (hbox), w); - g_signal_connect (w, "clicked", - G_CALLBACK (ircam_clicked), NULL); + w = gtk_toggle_button_new_with_label("Vis"); + gtk_container_add (GTK_CONTAINER (hbox), w); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(w), FALSE); + g_signal_connect (w, "clicked", + G_CALLBACK (viscam_clicked), NULL); - w = gtk_toggle_button_new_with_label("Vis"); - gtk_container_add (GTK_CONTAINER (hbox), w); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(w), FALSE); - g_signal_connect (w, "clicked", - G_CALLBACK (viscam_clicked), NULL); + // w = gtk_scale_new_with_range (GTK_ORIENTATION_VERTICAL, 0.0, 1.0, .01); - w = gtk_scale_new_with_range (GTK_ORIENTATION_VERTICAL, - 0.0, - 1.0, - .01); + psurface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 640, 500); - //psurface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 640, 500); - psurface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 640, 500); + image_darea = gtk_drawing_area_new (); + gtk_widget_set_size_request (image_darea, 640, 500); + gtk_container_add (GTK_CONTAINER (box), image_darea); - image_darea = gtk_drawing_area_new (); - /* set a minimum size */ - gtk_widget_set_size_request (image_darea, 640, 500); + g_signal_connect (image_darea, "draw", + G_CALLBACK (draw_event), NULL); +// g_signal_connect (image_darea,"configure-event", +// G_CALLBACK (configure_event), NULL); - gtk_container_add (GTK_CONTAINER (box), image_darea); + // camera-photo + w = gtk_button_new_from_icon_name("camera-photo", GTK_ICON_SIZE_DND); + gtk_container_add (GTK_CONTAINER (box), w); - g_signal_connect (image_darea, "draw", - G_CALLBACK (draw_event), NULL); - g_signal_connect (image_darea,"configure-event", - G_CALLBACK (configure_event), NULL); + g_signal_connect (w, "clicked", + G_CALLBACK (store_shot_clicked), NULL); - // camera-photo - w = gtk_button_new_from_icon_name("camera-photo", GTK_ICON_SIZE_DND); - gtk_container_add (GTK_CONTAINER (box), w); - - g_signal_connect (w, "clicked", - G_CALLBACK (store_shot_clicked), NULL); - - // g_timeout_add_seconds(1, handle_timeout, NULL); - - gtk_widget_show_all(window); - } + gtk_widget_show_all(window); return window; } diff --git a/plank.h b/plank.h index 81558d3..2aa8269 100644 --- a/plank.h +++ b/plank.h @@ -8,4 +8,11 @@ #define PlanckR2 0.012258549 #define TempReflected 20.0 // Reflected Apparent Temperature [°C] + +// 0.01 to 0.99 on the emissivity scale. +// Highly polished metallic surfaces such as copper or aluminum usually have an emissivity below 0.10. +// Roughened or oxidized metallic surfaces will have a much higher emissivity +// (0.6 or greater depending on the surface condition and the amount of oxidation). +// Most flat-finish paints are around 0.90, while human skin and water are about 0.98. + #define Emissivity 0.95 // Emissivity of object