Skip to content

Commit 5dafc9e

Browse files
P2 Pro camera support (#9)
* fix the warning about compiled library not being 16k aligned * ignore local.properties file * support p2pro camera * unit pedantry * fix hacked logic so infiray cameras continue to work * ignore temp file * fix min and max temperature not working with locked range
1 parent a86080c commit 5dafc9e

File tree

13 files changed

+127
-53
lines changed

13 files changed

+127
-53
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
.gradle
33
build/
44
/app/release
5-
5+
local.properties
6+
temp

app/src/main/java/be/ntmn/inficam/MainActivity.java

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ public class MainActivity extends BaseActivity {
9191
private int imgQuality;
9292

9393
private boolean raw_cam = false;
94+
private boolean p2Pro = false;
9495
private boolean first_connect = false;
9596

9697
private Bitmap imgCompressBitmap;
@@ -164,11 +165,13 @@ public void onDeviceFound(UsbDevice dev) {
164165
* and subclass are checked because older android versions don't filter for us.
165166
*/
166167
if (device != null || dev.getDeviceClass() != 239 || dev.getDeviceSubclass() != 2 ||
167-
(dev.getVendorId() != 0x1514 && dev.getVendorId() != 0x4B4 && dev.getVendorId() != 0x3474))
168+
(dev.getVendorId() != 0x1514 && dev.getVendorId() != 0x4B4 && dev.getVendorId() != 0x3474 && dev.getVendorId() != 0xBDA))
168169
return;
169170

170171
raw_cam = (dev.getVendorId() == 0x4B4) || (dev.getVendorId() == 0x3474); // For T2S+ A2 raw sensor
171172

173+
p2Pro = (dev.getVendorId() == 0xBDA); // flag for P2 Pro
174+
172175
device = dev;
173176
/* Connecting to a UVC device needs camera permission. */
174177
askPermission(Manifest.permission.CAMERA, granted -> {
@@ -185,6 +188,7 @@ public void onConnected(UsbDevice dev, UsbDeviceConnection conn) {
185188
disconnecting = false;
186189
earlyFrame = 0;
187190
try {
191+
infiCam.setP2Pro(p2Pro); // need to set p2Pro flag before connecting otherwise the resolution does not get set properly
188192
infiCam.connect(conn.getFileDescriptor());
189193
infiCam.setRawSensor(raw_cam);
190194
/* Size is only important for cubic interpolation. */
@@ -331,7 +335,31 @@ public void onFrame(InfiCam.FrameInfo fi, float[] temp) {
331335
fi.max_y = mma.max_y;
332336
rangeMax = mma.max;
333337
}
334-
}
338+
} else if (p2Pro) {
339+
// p2Pro does not report min and max through registers so use zoom logic to find min and max
340+
float lost = (1.0f - 1.0f / scale) / 2.0f;
341+
Overlay.mmaRect(mma, temp,
342+
(int) (lost * fi.width),
343+
(int) (lost * fi.height),
344+
(int) ((1.0f - lost) * fi.width),
345+
(int) ((1.0f - lost) * fi.height),
346+
fi.width);
347+
348+
fi.min = mma.min;
349+
fi.min_x = mma.min_x;
350+
fi.min_y = mma.min_y;
351+
if (isNaN(rangeMin)) {
352+
rangeMin = mma.min;
353+
}
354+
355+
fi.max = mma.max;
356+
fi.max_x = mma.max_x;
357+
fi.max_y = mma.max_y;
358+
if (isNaN(rangeMax)) {
359+
rangeMax = mma.max;
360+
}
361+
}
362+
335363

336364
infiCam.applyPalette(rangeMin, rangeMax);
337365
handler.post(handleFrameRunnable);

app/src/main/java/be/ntmn/inficam/Util.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,9 @@ public static void formatTemp(StringBuilder sb, float temp, int tempunit) {
199199
if (tempunit == TEMPUNIT_FAHRENHEIT)
200200
sb.append("°F");
201201
else if (tempunit == TEMPUNIT_KELVIN)
202-
sb.append("°K");
202+
sb.append("K"); // Fun Fact: Kelvin does not use degrees
203203
else if (tempunit == TEMPUNIT_RANKINE)
204-
sb.append("°R");
204+
sb.append("°R"); // Funner Fact: Rankine does use degrees
205205
else sb.append("°C");
206206
}
207207

libinficam/src/main/java/be/ntmn/libinficam/InfiCam.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ public native void setParams(float corr, float t_ref, float t_air, float humi, f
126126
public native void calibrate();
127127
public native void closeShutter();
128128
public native void setRawSensor(boolean raw);
129+
public native void setP2Pro(boolean p2Pro);
129130

130131
private native int nativeSetPalette(int[] palette); /* Length must be paletteLen. */
131132
public void setPalette(int[] palette) {

libinficam/src/main/jni/Application.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ APP_PLATFORM := android-16
22
APP_ABI := armeabi-v7a arm64-v8a x86_64 x86
33
APP_OPTIM := release
44
APP_STL := c++_static
5+
APP_SUPPORT_FLEXIBLE_PAGE_SIZES := true

libinficam/src/main/jni/InfiCam/InfiCam.cpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616

1717

1818
void InfiCam::uvc_callback(uvc_frame_t *frame, void *user_ptr) {
19+
// This function gets called every time a new frame is ready
1920
InfiCam *p = (InfiCam *) user_ptr;
2021
if (frame->data_bytes < p->dev.width * p->dev.height * 2)
2122
return;
23+
2224
if(p->calibrating) {
2325
size_t frame_size = p->dev.width * (p->dev.height - InfiCam::DATA_ROWS); // We don't want to calibrate out the data rows
2426

@@ -72,7 +74,13 @@ void InfiCam::uvc_callback(uvc_frame_t *frame, void *user_ptr) {
7274
if (p->intermediary_buffer == nullptr) {
7375
p->intermediary_buffer = new uint16_t[frame_size];
7476
}
75-
memcpy(p->intermediary_buffer, frame->data, frame_size * sizeof(uint16_t));
77+
78+
if (p->p2_pro) {
79+
memcpy(p->intermediary_buffer, (uint16_t *) frame->data + 256*192, 256*192 * sizeof(uint16_t)); // use only the half of the image with the thermal data
80+
} else {
81+
memcpy(p->intermediary_buffer, frame->data, frame_size * sizeof(uint16_t));
82+
}
83+
7684

7785
// Apply calibration and dead pixel correction in a single pass
7886
if(p->raw_sensor && p->calibrated) {
@@ -141,6 +149,7 @@ void InfiCam::uvc_callback(uvc_frame_t *frame, void *user_ptr) {
141149
p->infi.update_table(p->intermediary_buffer);
142150
p->table_invalid = 0;
143151
} else p->infi.update(p->intermediary_buffer);
152+
144153
p->infi.temp(p->intermediary_buffer, p->frame_temp);
145154

146155
/* Unlock before the callback so if it decides to call a function that locks the this callback
@@ -173,15 +182,15 @@ int InfiCam::connect(int fd) {
173182
*/
174183
if (pthread_mutex_init(&frame_callback_mutex, NULL))
175184
return 1;
176-
if (dev.connect(fd)) {
185+
if (dev.connect(fd, p2_pro)) {
177186
pthread_mutex_destroy(&frame_callback_mutex);
178187
return 2;
179188
}
180-
if (infi.init(dev.width, dev.height)) {
181-
dev.disconnect();
182-
pthread_mutex_destroy(&frame_callback_mutex);
183-
return 3;
184-
}
189+
if (infi.init(dev.width, dev.height)) {
190+
dev.disconnect();
191+
pthread_mutex_destroy(&frame_callback_mutex);
192+
return 3;
193+
}
185194
dev.set_zoom_abs(CMD_MODE_TEMP);
186195
connected = 1;
187196
set_range(infi.range);

libinficam/src/main/jni/InfiCam/InfiCam.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class InfiCam {
2727
pthread_cond_t calibration_cond;
2828
int connected = 0, streaming = 0, table_invalid;
2929
bool raw_sensor = false;
30+
bool p2_pro;
3031
bool calibrating = false;
3132
bool calibrated = false;
3233
bool* dead_pixel_mask = nullptr;
@@ -102,6 +103,7 @@ class InfiCam {
102103
void set_params(float corr, float t_ref, float t_air, float humi, float emi, float dist);
103104
void store_params(); /* Store user memory to camera so values remain when reconnecting. */
104105
void set_raw_sensor(bool raw) { raw_sensor = raw; };
106+
void set_p2_pro(bool p2_pro_dev) { p2_pro = p2_pro_dev; };
105107

106108
void update_table();
107109
void calibrate();

libinficam/src/main/jni/InfiCam/InfiCamJNI.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,15 @@ JNIEXPORT void Java_be_ntmn_libinficam_InfiCam_setRawSensor(JNIEnv *env, jobject
353353
InfiCam *cam = getObject(env, self);
354354
if (cam != NULL) {
355355
cam->set_raw_sensor(raw);
356-
cam->infi.raw_sensor = true;
356+
cam->infi.raw_sensor = raw;
357+
}
358+
}
359+
360+
JNIEXPORT void Java_be_ntmn_libinficam_InfiCam_setP2Pro(JNIEnv *env, jobject self, jboolean p2_pro) {
361+
InfiCam *cam = getObject(env, self);
362+
if (cam != NULL) {
363+
cam->set_p2_pro(p2_pro);
364+
cam->infi.p2_pro = p2_pro;
357365
}
358366
}
359367

@@ -415,3 +423,4 @@ JNIEXPORT void Java_be_ntmn_libinficam_InfiCam_applyPalette(JNIEnv *env, jobject
415423
}
416424

417425
} /* extern "C" */
426+

libinficam/src/main/jni/InfiCam/InfiFrame.cpp

Lines changed: 52 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,13 @@ static inline float atmt(float h, float t_atm, float d) {
4343
}
4444

4545
int InfiFrame::init(int width, int height) {
46-
this->width = width;
47-
this->height = height - 4;
46+
if (p2_pro) {
47+
this->width = 256;
48+
this->height = 192; //384
49+
} else {
50+
this->width = width;
51+
this->height = height - 4;
52+
}
4853
s1_offset = width * (height - 4);
4954

5055
cal_00_offset = 390.0;
@@ -119,16 +124,8 @@ void InfiFrame::update(uint16_t *frame) {
119124
float cal_03 = read_float(frame + s2_offset, 7);
120125
float cal_04 = read_float(frame + s2_offset, 9);
121126
float cal_05 = read_float(frame + s2_offset, 11);
122-
if(!raw_sensor) {
123-
temp_max_x = read_u16(frame + s1_offset, 2);
124-
temp_max_y = read_u16(frame + s1_offset, 3);
125-
temp_max = read_u16(frame + s1_offset, 4);
126-
temp_min_x = read_u16(frame + s1_offset, 5);
127-
temp_min_y = read_u16(frame + s1_offset, 6);
128-
temp_min = read_u16(frame + s1_offset, 7);
129-
temp_avg = read_u16(frame + s1_offset, 8);
130-
temp_center = read_u16(frame + s1_offset, 12);
131-
}else{
127+
128+
if (raw_sensor) {
132129
temp_max = 0;
133130
temp_min = UINT16_MAX;
134131
uint32_t sum = 0;
@@ -162,6 +159,17 @@ void InfiFrame::update(uint16_t *frame) {
162159
}
163160

164161
temp_avg = sum / (height * width);
162+
} else if (p2_pro) {
163+
temp_center = frame[96 * width + 128]>>2;
164+
} else {
165+
temp_max_x = read_u16(frame + s1_offset, 2);
166+
temp_max_y = read_u16(frame + s1_offset, 3);
167+
temp_max = read_u16(frame + s1_offset, 4);
168+
temp_min_x = read_u16(frame + s1_offset, 5);
169+
temp_min_y = read_u16(frame + s1_offset, 6);
170+
temp_min = read_u16(frame + s1_offset, 7);
171+
temp_avg = read_u16(frame + s1_offset, 8);
172+
temp_center = read_u16(frame + s1_offset, 12);
165173
}
166174
temp_user[0] = read_u16(frame + s1_offset, 13);
167175
temp_user[1] = read_u16(frame + s1_offset, 14);
@@ -191,10 +199,15 @@ float InfiFrame::temp_single(uint16_t x) {
191199
/* Temperatures below what we can calculate result in sqrt of a negative number, the absolute
192200
* lowest temperature we can calculate is the one we get if we set n to 0.
193201
*/
194-
float n = sqrtf(((float) (x - table_offset) * cal_d + cal_c) / cal_01 + cal_b);
195-
float wtot = powf((isfinite(n) ? n : 0.0) - cal_a + zeroc, 4);
196-
float ttot = powf((wtot - numerator_sub) / denominator, 0.25) - zeroc;
197-
return ttot + (distance_adjusted * 0.85 - 1.125) * (ttot - temp_air) / 100.0 + correction;
202+
203+
if (p2_pro) {
204+
return ((float) x )/16.0f-273.15f;
205+
} else {
206+
float n = sqrtf(((float) (x - table_offset) * cal_d + cal_c) / cal_01 + cal_b);
207+
float wtot = powf((isfinite(n) ? n : 0.0) - cal_a + zeroc, 4);
208+
float ttot = powf((wtot - numerator_sub) / denominator, 0.25) - zeroc;
209+
return ttot + (distance_adjusted * 0.85 - 1.125) * (ttot - temp_air) / 100.0 + correction;
210+
}
198211
}
199212

200213
void InfiFrame::update_table(uint16_t *frame) {
@@ -209,23 +222,33 @@ void InfiFrame::temp(uint16_t *input, float *output) {
209222

210223
void InfiFrame::temp(uint16_t *input, float *output, size_t len) {
211224
for (size_t i = 0; i < len; ++i)
212-
output[i] = temp(input[i]);
225+
output[i] = temp(input[i]>>2);
213226
}
214227

215228
void InfiFrame::read_params(uint16_t *frame) {
216229
/* Presumeably this is just a 128 byte ram+eeprom area. */
217-
if(!raw_sensor) {
218-
correction = read_float(frame + s2_offset, 127);
219-
/* NOTE Original Infiray software uses a uint16 for distance. */
220-
distance = read_float(frame + s2_offset, 137);
221-
}else{
222-
// TODO: Handling of theve vraibles and user input should be done within the app because writing to these doesn't work on the T2S+ A2
223-
distance = (float)read_u16(frame + s2_offset, 137);
230+
if (p2_pro) {
231+
correction = 0;
232+
distance = 0;
233+
temp_reflected = 0;
234+
temp_air = 0;
235+
humidity = 0;
236+
emissivity = 1.0;
237+
} else {
238+
if (!raw_sensor) {
239+
correction = read_float(frame + s2_offset, 127);
240+
/* NOTE Original Infiray software uses a uint16 for distance. */
241+
distance = read_float(frame + s2_offset, 137);
242+
} else {
243+
// TODO: Handling of theve vraibles and user input should be done within the app because writing to these doesn't work on the T2S+ A2
244+
distance = (float) read_u16(frame + s2_offset, 137);
245+
}
246+
temp_reflected = read_float(frame + s2_offset, 129);
247+
temp_air = read_float(frame + s2_offset, 131);
248+
humidity = read_float(frame + s2_offset, 133);
249+
emissivity = read_float(frame + s2_offset, 135);
224250
}
225-
temp_reflected = read_float(frame + s2_offset, 129);
226-
temp_air = read_float(frame + s2_offset, 131);
227-
humidity = read_float(frame + s2_offset, 133);
228-
emissivity = read_float(frame + s2_offset, 135);
251+
229252
}
230253

231254
void InfiFrame::read_version(uint16_t *frame, char *product, char *serial, char *fw_version) {
@@ -264,7 +287,7 @@ void InfiFrame::palette_appy(float *input, uint32_t *output, size_t len) {
264287
palette_appy(input, output, len, temp(temp_min), temp(temp_max));
265288
}
266289

267-
void InfiFrame::palette_appy(float *input, uint32_t *output, float min, float max) {
290+
void InfiFrame::palette_appy(float *input, uint32_t *output, float min, float max) { // TODO this palette_appy gets used
268291
palette_appy(input, output, width * height, min, max);
269292
}
270293

libinficam/src/main/jni/InfiCam/InfiFrame.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ class InfiFrame {
2121
public:
2222
/* Defines if the camera reports raw sensor readings such as the T2S+ A2 version. */
2323
bool raw_sensor = false;
24+
/* Defines if the camera is the P2 Pro. */
25+
bool p2_pro = false;
2426
/* Dimensions of actual thermographic image, set by init(). */
2527
int width = 0, height = 0; /* And set here too for predictability. */
2628

0 commit comments

Comments
 (0)