start parsing meta information from JSON objects coming through endpoints,
like shutter status and battery status
This commit is contained in:
parent
654ba67237
commit
ab5cbe7d6f
7 changed files with 461 additions and 122 deletions
4
Makefile
4
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
|
||||
|
|
128
NOTES.txt
128
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
|
||||
<cc><01><00><00><01><00><00><00>U<00><00><00>0s<8c><df>{"type":"batteryVoltageUpdate","data":{"voltage":4.11999988555908,"percentage":100}}<00>
|
||||
EP81=0 in 100
|
||||
<cc><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
|
||||
<cc><01><00><00><01><00><00><00>U<00><00><00>0s<8c><df>{"type":"batteryVoltageUpdate","data":{"voltage":4.11999988555908,"percentage":100}}<00>
|
||||
EP81=0 in 100
|
||||
<cc><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
|
||||
<cc><01><00><00><01><00><00><00>U<00><00><00>0s<8c><df>{"type":"batteryVoltageUpdate","data":{"voltage":4.11999988555908,"percentage":100}}<00>
|
||||
EP81=0 in 100
|
||||
<cc><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
|
||||
<cc><01><00><00><01><00><00><00>U<00><00><00>0s<8c><df>{"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
|
||||
<cc><01><00><00><01><00><00><00>U<00><00><00>0s<8c><df>{"type":"batteryVoltageUpdate","data":{"voltage":4.13000011444092,"percentage":100}}<00><cc><01><00><00><01><00><00><00>H<00><00><00>r<fc><ff>}{"type":"batteryChargingCurrentUpdate","data":{"chargingCurrent":1000}}<00><cc><01><00><00><01><00><00><00>Y<00><00><00><88><cc>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"
|
||||
}
|
||||
|
|
15
README.md
15
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
|
||||
|
|
208
cam-thread.c
208
cam-thread.c
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <cJSON.h>
|
||||
|
||||
// from main thread
|
||||
void update_fb(void);
|
||||
|
@ -28,6 +29,7 @@ extern unsigned char *color_palette;
|
|||
|
||||
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
@ -41,19 +43,17 @@ extern unsigned char *color_palette;
|
|||
#include <fcntl.h>
|
||||
#include <math.h>
|
||||
|
||||
#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<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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
31
cam-thread.h
Normal file
31
cam-thread.h
Normal file
|
@ -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
|
190
flirgtk.c
190
flirgtk.c
|
@ -25,6 +25,8 @@
|
|||
#include <time.h>
|
||||
// #include <limits.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
|
7
plank.h
7
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
|
||||
|
|
Loading…
Reference in a new issue