Loading src/main/java/com/elphel/imagej/readers/Boson640Telemetry.java 0 → 100644 +192 −0 Original line number Diff line number Diff line /** ** -----------------------------------------------------------------------------** ** Boson640Telemetry.java ** ** Parses FLIR Boson640 telemetry data ** ** Copyright (C) 2021 Elphel, Inc. ** ** -----------------------------------------------------------------------------** ** ** Boson640Telemetry.java is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see <http://www.gnu.org/licenses/>. ** -----------------------------------------------------------------------------** ** */ package com.elphel.imagej.readers; import java.nio.ByteBuffer; import java.util.HashMap; public class Boson640Telemetry { private static final int BOSON640_IMAGE_WIDTH = 640; private static final int BOSON640_IMAGE_HEIGHT = 512; private static final int BOSON640_TELEMETRY_LINES = 1; private static final String REVISION = "REVISION"; private static final int REVISION_OFFSET = 0; // 2 private static final String CAMSERIAL = "CAMSERIAL"; private static final int CAMSERIAL_OFFSET = 2; // 4 private static final String SENSSERIAL = "SENSSERIAL"; private static final int SENSSERIAL_OFFSET = 6; // 4 private static final String PART_ASCII = "PART_ASCII"; private static final int PART_ASCII_OFFSET = 10; // 20 private static final String SOFT_REV = "SOFT_REV"; private static final int SOFT_REV_OFFSET = 44; // 12 private static final String FRAME_RATE = "FRAME_RATE"; private static final int FRAME_RATE_OFFSET = 56; // 2 private static final String STATUS = "STATUS"; private static final int STATUS_OFFSET = 76; // hex,8 private static final String FRAME = "FRAME"; private static final int FRAME_OFFSET = 84; // 4 private static final String FRAME_FFC = "FRAME_FFC"; private static final int FRAME_FFC_OFFSET = 88; // 4 private static final String FPA_KELV = "FPA_KELV"; private static final int FPA_KELV_OFFSET = 94; // 2 private static final String FFC_KELV = "FFC_KELV"; private static final int FFC_KELV_OFFSET = 96; // 2 private static final String PIPELINE = "PIPELINE"; private static final int PIPELINE_OFFSET = 110; // 4 private static final String FFC_INTEG = "FFC_INTEG"; private static final int FFC_INTEG_OFFSET = 114; // 2 private static final String NUC_CURRENT = "NUC_CURRENT"; private static final int NUC_CURRENT_OFFSET = 158; // 2 private static final String NUC_DESIRED = "NUC_DESIRED"; private static final int NUC_DESIRED_OFFSET = 160; // 2 private static final String CORE_TEMP = "CORE_TEMP"; private static final int CORE_TEMP_OFFSET = 162; // 4 private static final String OVERTEMP = "OVERTEMP"; private static final int OVERTEMP_OFFSET = 166; // 4 private static final String ROI_POP_LTH = "ROI_POP_LTH"; private static final int ROI_POP_LTH_OFFSET = 170; // 4 private static final String ROI_POP_HTL = "ROI_POP_HTL"; private static final int ROI_POP_HTL_OFFSET = 174; // 4 private static final String TGL_PATT = "TGL_PATT"; private static final int TGL_PATT_OFFSET = 178; // 6 private static final String ZOOM_FACT = "ZOOM_FACT"; private static final int ZOOM_FACT_OFFSET = 184; // 4 private static final String ZOOM_X0 = "ZOOM_X0"; private static final int ZOOM_X0_OFFSET = 188; // 4 private static final String ZOOM_Y0 = "ZOOM_Y0"; private static final int ZOOM_Y0_OFFSET = 192; // 4 private float [] pixels = null; private byte [] btm = null; private ByteBuffer bbtm = null; public Boson640Telemetry(ByteBuffer bb, float [] pixels, boolean bottom) { this.pixels = pixels; int offset = 2*(bottom?(BOSON640_IMAGE_WIDTH * BOSON640_IMAGE_HEIGHT):0); if (pixels.length > BOSON640_IMAGE_WIDTH*BOSON640_IMAGE_HEIGHT) { this.btm = new byte [BOSON640_IMAGE_WIDTH* BOSON640_TELEMETRY_LINES * 2]; bb.position(offset); byte dbg = bb.get(); dbg = bb.get(); dbg = bb.get(); dbg = bb.get(); dbg = bb.get(); bb.position(offset); // this.bbtm = bb.get(btm, 0,btm.length); this.bbtm = ByteBuffer.wrap(btm); this.bbtm.order( bb.order()); // or is it already same as in bb? System.out.println("getShort(94)"+this.bbtm.getShort(94)); System.out.println("getLong(162)"+this.bbtm.getLong(162)); System.out.println("getLong(162)"+this.bbtm.getInt(162)); this.pixels = new float [BOSON640_IMAGE_WIDTH * BOSON640_IMAGE_HEIGHT]; System.arraycopy( pixels, (bottom? 0: BOSON640_IMAGE_WIDTH * BOSON640_TELEMETRY_LINES), this.pixels, 0, this.pixels.length); } } public static int getWidth() { return BOSON640_IMAGE_WIDTH; } public static int getHeight() { return BOSON640_IMAGE_HEIGHT; } public static int getTelemetryLines() { return BOSON640_TELEMETRY_LINES; } public boolean hasTelemetry() { return bbtm != null; } public float [] getPixels() { return pixels; } public HashMap<String, String> parseTelemetry(){ HashMap<String, String> tm = new HashMap<String, String>(); byte [] bytes = getBytes(REVISION_OFFSET,2); tm.put(REVISION, "" + bytes[0] + "." + bytes[1]); // "0.2" tm.put(CAMSERIAL, "" + String.format("%d", getU32 (CAMSERIAL_OFFSET))); tm.put(SENSSERIAL, "" + String.format("%d", getU32 (SENSSERIAL_OFFSET))); tm.put(PART_ASCII, "" + getAscii(PART_ASCII_OFFSET,20)); tm.put(STATUS, "" + (bbtm.getLong(STATUS_OFFSET))); // getHex (STATUS_OFFSET,8)); tm.put(SOFT_REV, "" + getU32 (SOFT_REV_OFFSET)+"."+getU32 (SOFT_REV_OFFSET+4)+"."+getU32 (SOFT_REV_OFFSET+8)); tm.put(FRAME_RATE, "" + getU16 (FRAME_RATE_OFFSET)); tm.put(FRAME, "" + getU32 (FRAME_OFFSET)); tm.put(FRAME_FFC, "" + getU32 (FRAME_FFC_OFFSET)); tm.put(FPA_KELV, String.format("%.1f", getKelv10 (FPA_KELV_OFFSET))); tm.put(FFC_KELV, String.format("%.1f", getKelv10 (FFC_KELV_OFFSET))); tm.put(PIPELINE, "" + getU32 (PIPELINE_OFFSET)); tm.put(FFC_INTEG, "" + getU16 (FFC_INTEG_OFFSET)); tm.put(NUC_CURRENT, "" + getU16 (NUC_CURRENT_OFFSET)); tm.put(NUC_DESIRED, "" + getU16 (NUC_DESIRED_OFFSET)); tm.put(CORE_TEMP, String.format("%.3f", getCelsius1000 (CORE_TEMP_OFFSET))); tm.put(OVERTEMP, "" + getU32 (OVERTEMP_OFFSET)); tm.put(ROI_POP_LTH, "" + getU32 (ROI_POP_LTH_OFFSET)); tm.put(ROI_POP_HTL, "" + getU32 (ROI_POP_HTL_OFFSET)); tm.put(TGL_PATT, "" + getHex (TGL_PATT_OFFSET,8)); tm.put(ZOOM_FACT, "" + getU32 (ZOOM_FACT_OFFSET)); tm.put(ZOOM_X0, "" + getU32 (ZOOM_X0_OFFSET)); tm.put(ZOOM_Y0, "" + getU32 (ZOOM_Y0_OFFSET)); return tm; } // internal methods private long getU32(int offset) { return ((long) (bbtm.getInt(offset))) & 0xffffffff; } private long getU16(int offset) { return ((long) (bbtm.getShort(offset))) & 0xffff; } private byte[] getBytes (int offset, int len) { // offset, len in bytes! byte [] bytes = new byte [len]; for (int i = 0; i<len; i++) { bytes[i] = bbtm.get(offset+i); } return bytes; } private String getHex (int offset, int len) { // offset, len bytes, String s = ""; for (byte b :getBytes(offset, len)) { s += String.format("%02x", b); } return s; } private String getAscii (int offset, int len) { // offset, len bytes, String s = ""; for (byte b :getBytes(offset, len)) { if (b == 0) break; s += String.format("%c", b); } return s; } private double getKelv10(int offset) { return 0.1*getU16(offset); } private double getCelsius1000(int offset) { return 0.001*bbtm.getInt(offset); } } src/main/java/com/elphel/imagej/readers/ImagejJp4Tiff.java +41 −6 Original line number Diff line number Diff line Loading @@ -206,24 +206,49 @@ public class ImagejJp4Tiff { Hashtable<String, Object> meta_hash = reader.getGlobalMetadata(); boolean degamma = bytes_per_pixel < 2; // both JP4 and 8-bit tiff // FIXME: Do not scale telemetry! /* if (deGammaScale(pixels, reader.getSizeX(), meta_hash, degamma, scale ) == null) { LOGGER.error("Problem degamma/scaling of "+content_fileName); } */ boolean telemetry = (bytes_per_pixel == 2); boolean lepton_telemetry = telemetry && ((reader.getSizeX() == 160)); boolean boson_telemetry = telemetry && ((reader.getSizeX() == 640)); Lepton3Telemetry lepton3Telemetry = null; Boson640Telemetry boson640Telemetry = null; if (telemetry) { if (lepton_telemetry) { lepton3Telemetry = new Lepton3Telemetry(bb,pixels, true); // bottom telemetry = lepton3Telemetry.hasTelemetry(); } else if (boson_telemetry) { boson640Telemetry = new Boson640Telemetry(bb,pixels, false); // top telemetry = boson640Telemetry.hasTelemetry(); } } ImageProcessor ip = null; if (telemetry) { ip=new FloatProcessor(Lepton3Telemetry.LEPTON3_IMAGE_WIDTH, Lepton3Telemetry.LEPTON3_IMAGE_HEIGHT); ip.setPixels(lepton3Telemetry.getPixels()); // bo if (lepton_telemetry) { ip=new FloatProcessor(Lepton3Telemetry.getWidth(), Lepton3Telemetry.getHeight()); pixels = lepton3Telemetry.getPixels(); // ip.setPixels(lepton3Telemetry.getPixels()); // bo } else if (boson_telemetry) { ip=new FloatProcessor(Boson640Telemetry.getWidth(), Boson640Telemetry.getHeight()); pixels=boson640Telemetry.getPixels(); // bo // ip.setPixels(boson640Telemetry.getPixels()); // bo } } else { ip=new FloatProcessor(reader.getSizeX(), reader.getSizeY()); ip.setPixels(pixels); // ip.setPixels(pixels); } if (deGammaScale(pixels, reader.getSizeX(), meta_hash, degamma, scale ) == null) { LOGGER.error("Problem degamma/scaling of "+content_fileName); } ip.setPixels(pixels); ip.resetMinAndMax(); String prefix = ElphelTiffReader.ELPHEL_PROPERTY_PREFIX; String imageName = content_fileName; // path; Loading @@ -247,12 +272,18 @@ public class ImagejJp4Tiff { imp.setProperty(std+(key.replace(" ","_").replace("/","_")), meta_hash.get(key).toString()); } } if (telemetry) { if (lepton_telemetry) { HashMap<String, String> telemetryMap = lepton3Telemetry.parseTelemetry(); for (String key:telemetryMap.keySet()) { imp.setProperty(TELEMETRY_PREFIX+key, telemetryMap.get(key)); } } if (boson_telemetry) { HashMap<String, String> telemetryMap = boson640Telemetry.parseTelemetry(); for (String key:telemetryMap.keySet()) { imp.setProperty(TELEMETRY_PREFIX+key, telemetryMap.get(key)); } } encodeProperiesToInfo(imp); Location.mapFile(content_fileName, null); return imp; Loading Loading @@ -368,6 +399,10 @@ public class ImagejJp4Tiff { for (int x = 0; x < width; x+=2) { for (int dx = 0; dx<2; dx++) { int indx = base + x + dx; if (indx >= pixels.length) { System.out.println(indx); continue; } pixels[indx] *= rgains[(dy << 1) + 1 - dx]; } } Loading src/main/java/com/elphel/imagej/readers/Lepton3Telemetry.java +58 −47 Original line number Diff line number Diff line Loading @@ -29,53 +29,53 @@ import java.nio.ByteBuffer; import java.util.HashMap; public class Lepton3Telemetry { public static final int LEPTON3_IMAGE_WIDTH = 160; public static final int LEPTON3_IMAGE_HEIGHT = 120; public static final int LEPTON3_TELEMETRY_LINES = 2; public static final String REVISION = "REVISION"; public static final int REVISION_OFFSET = 0; public static final String UPTIME = "UPTIME"; public static final int UPTIME_OFFSET = 1; public static final String STATUS = "STATUS"; public static final int STATUS_OFFSET = 3; public static final String MOD_SER = "MOD_SER"; public static final int MOD_SER_OFFSET = 5; public static final String SOFT_REV = "SOFT_REV"; public static final int SOFT_REV_OFFSET = 13; public static final String FRAME = "FRAME"; public static final int FRAME_OFFSET = 20; public static final String MEAN = "MEAN"; public static final int MEAN_OFFSET = 22; public static final String FPA_TCNT = "FPA_TCNT"; public static final int FPA_TCNT_OFFSET = 23; public static final String FPA_KELV = "FPA_KELV"; public static final int FPA_KELV_OFFSET = 24; public static final String ENCL_TCNT = "ENCL_TCNT"; public static final int ENCL_TCNT_OFFSET = 25; public static final String ENCL_KELV = "ENCL_KELV"; public static final int ENCL_KELV_OFFSET = 26; public static final String FFC_KELV = "FFC_KELV"; public static final int FFC_KELV_OFFSET = 29; public static final String FFC_TIME = "FFC_TIME"; public static final int FFC_TIME_OFFSET = 30; public static final String ENCL_FFC_KELV = "ENCL_FFC_KELV"; public static final int ENCL_FFC_KELV_OFFSET = 32; public static final String AGC_ROI_TLBR = "AGC_ROI_TLBR"; public static final int AGC_ROI_TLBR_OFFSET = 34; public static final String AGC_CLIP_HIGH = "AGC_CLIP_HIGH"; public static final int AGC_CLIP_HIGH_OFFSET = 38; public static final String AGC_CLIP_LOW = "AGC_CLIP_LOW"; public static final int AGC_CLIP_LOW_OFFSET = 38; public static final String VFORMAT = "VFORMAT"; public static final int VFORMAT_OFFSET = 72; public static final String FFC_LOG2 = "FFC_LOG2"; public static final int FFC_LOG2_OFFSET = 74; public static final String EMISS_8192 = "EMISS_8192"; public static final int EMISS_8192_OFFSET = 19 + 80; // row B public static final String BGND_KELV = "BGND_KELV"; public static final int BGND_KELV_OFFSET = 20 + 80; public static final String ATMOSPH_8192 = "ATMOSPH_8192"; public static final int ATMOSPH_8192_OFFSET = 21 + 80; public static final String ATMOSPH_KELV = "ATMOSPH_KELV"; public static final int ATMOSPH_KELV_OFFSET = 22 + 80; public static final String WND_TRANS_8192 ="WND_TRANS_8192";public static final int WND_TRANS_8192_OFFSET =23 + 80; public static final String WND_REFL_8192 = "WND_REFL_8192"; public static final int WND_REFL_8192_OFFSET = 24 + 80; public static final String WND_KELV = "WND_KELV"; public static final int WND_KELV_OFFSET = 25 + 80; public static final String WND_REFL_KELV = "WND_REFL_KELV"; public static final int WND_REFL_KELV_OFFSET = 26 + 80; public static final String GAIN_MODE = "GAIN_MODE"; public static final int GAIN_MODE_OFFSET = 5 + 160; // row C public static final String GAIN_EFF = "GAIN_EFF"; public static final int GAIN_EFF_OFFSET = 6 + 160; public static final String GAIN_DFLAG = "GAIN_DFLAG"; public static final int GAIN_DFLAG_OFFSET = 7 + 160; public static final String GAIN_THR_HL_C = "GAIN_THR_HL_C"; public static final int GAIN_THR_HL_C_OFFSET = 8 + 160; public static final String GAIN_THR_LH_C = "GAIN_THR_LH_C"; public static final int GAIN_THR_LH_C_OFFSET = 9 + 160; public static final String GAIN_THR_HL_K = "GAIN_THR_HL_K"; public static final int GAIN_THR_HL_K_OFFSET = 10 + 160; public static final String GAIN_THR_LH_K = "GAIN_THR_LH_K"; public static final int GAIN_THR_LH_K_OFFSET = 11 + 160; public static final String GAIN_HL_PP = "GAIN_HL_PP"; public static final int GAIN_HL_PP_OFFSET = 14 + 160; public static final String GAIN_LH_PP = "GAIN_LH_PP"; public static final int GAIN_LH_PP_OFFSET = 15 + 160; public static final String GAIN_ROI_TLBR = "GAIN_ROI_TLBR"; public static final int GAIN_ROI_TLBR_OFFSET = 22 + 160; public static final String TLIN_EN = "TLIN_EN"; public static final int TLIN_EN_OFFSET = 48 + 160; public static final String TLIN_RESOL = "TLIN_RESOL"; public static final int TLIN_RESOL_OFFSET = 49 + 160; public static final String SPOT_AVG_KELV = "SPOT_AVG_KELV"; public static final int SPOT_AVG_KELV_OFFSET = 50 + 160; public static final String SPOT_MAX_KELV = "SPOT_MAX_KELV"; public static final int SPOT_MAX_KELV_OFFSET = 51 + 160; public static final String SPOT_MIN_KELV = "SPOT_MIN_KELV"; public static final int SPOT_MIN_KELV_OFFSET = 52 + 160; public static final String SPOT_POP_PERC = "SPOT_POP_PERC"; public static final int SPOT_POP_PERC_OFFSET = 53 + 160; public static final String SPOT_ROI_TLBR = "SPOT_ROI_TLBR"; public static final int SPOT_ROI_TLBR_OFFSET = 54 + 160; private static final int LEPTON3_IMAGE_WIDTH = 160; private static final int LEPTON3_IMAGE_HEIGHT = 120; private static final int LEPTON3_TELEMETRY_LINES = 2; private static final String REVISION = "REVISION"; private static final int REVISION_OFFSET = 0; private static final String UPTIME = "UPTIME"; private static final int UPTIME_OFFSET = 1; private static final String STATUS = "STATUS"; private static final int STATUS_OFFSET = 3; private static final String MOD_SER = "MOD_SER"; private static final int MOD_SER_OFFSET = 5; private static final String SOFT_REV = "SOFT_REV"; private static final int SOFT_REV_OFFSET = 13; private static final String FRAME = "FRAME"; private static final int FRAME_OFFSET = 20; private static final String MEAN = "MEAN"; private static final int MEAN_OFFSET = 22; private static final String FPA_TCNT = "FPA_TCNT"; private static final int FPA_TCNT_OFFSET = 23; private static final String FPA_KELV = "FPA_KELV"; private static final int FPA_KELV_OFFSET = 24; private static final String ENCL_TCNT = "ENCL_TCNT"; private static final int ENCL_TCNT_OFFSET = 25; private static final String ENCL_KELV = "ENCL_KELV"; private static final int ENCL_KELV_OFFSET = 26; private static final String FFC_KELV = "FFC_KELV"; private static final int FFC_KELV_OFFSET = 29; private static final String FFC_TIME = "FFC_TIME"; private static final int FFC_TIME_OFFSET = 30; private static final String ENCL_FFC_KELV = "ENCL_FFC_KELV"; private static final int ENCL_FFC_KELV_OFFSET = 32; private static final String AGC_ROI_TLBR = "AGC_ROI_TLBR"; private static final int AGC_ROI_TLBR_OFFSET = 34; private static final String AGC_CLIP_HIGH = "AGC_CLIP_HIGH"; private static final int AGC_CLIP_HIGH_OFFSET = 38; private static final String AGC_CLIP_LOW = "AGC_CLIP_LOW"; private static final int AGC_CLIP_LOW_OFFSET = 38; private static final String VFORMAT = "VFORMAT"; private static final int VFORMAT_OFFSET = 72; private static final String FFC_LOG2 = "FFC_LOG2"; private static final int FFC_LOG2_OFFSET = 74; private static final String EMISS_8192 = "EMISS_8192"; private static final int EMISS_8192_OFFSET = 19 + 80; // row B private static final String BGND_KELV = "BGND_KELV"; private static final int BGND_KELV_OFFSET = 20 + 80; private static final String ATMOSPH_8192 = "ATMOSPH_8192"; private static final int ATMOSPH_8192_OFFSET = 21 + 80; private static final String ATMOSPH_KELV = "ATMOSPH_KELV"; private static final int ATMOSPH_KELV_OFFSET = 22 + 80; private static final String WND_TRANS_8192 ="WND_TRANS_8192";private static final int WND_TRANS_8192_OFFSET =23 + 80; private static final String WND_REFL_8192 = "WND_REFL_8192"; private static final int WND_REFL_8192_OFFSET = 24 + 80; private static final String WND_KELV = "WND_KELV"; private static final int WND_KELV_OFFSET = 25 + 80; private static final String WND_REFL_KELV = "WND_REFL_KELV"; private static final int WND_REFL_KELV_OFFSET = 26 + 80; private static final String GAIN_MODE = "GAIN_MODE"; private static final int GAIN_MODE_OFFSET = 5 + 160; // row C private static final String GAIN_EFF = "GAIN_EFF"; private static final int GAIN_EFF_OFFSET = 6 + 160; private static final String GAIN_DFLAG = "GAIN_DFLAG"; private static final int GAIN_DFLAG_OFFSET = 7 + 160; private static final String GAIN_THR_HL_C = "GAIN_THR_HL_C"; private static final int GAIN_THR_HL_C_OFFSET = 8 + 160; private static final String GAIN_THR_LH_C = "GAIN_THR_LH_C"; private static final int GAIN_THR_LH_C_OFFSET = 9 + 160; private static final String GAIN_THR_HL_K = "GAIN_THR_HL_K"; private static final int GAIN_THR_HL_K_OFFSET = 10 + 160; private static final String GAIN_THR_LH_K = "GAIN_THR_LH_K"; private static final int GAIN_THR_LH_K_OFFSET = 11 + 160; private static final String GAIN_HL_PP = "GAIN_HL_PP"; private static final int GAIN_HL_PP_OFFSET = 14 + 160; private static final String GAIN_LH_PP = "GAIN_LH_PP"; private static final int GAIN_LH_PP_OFFSET = 15 + 160; private static final String GAIN_ROI_TLBR = "GAIN_ROI_TLBR"; private static final int GAIN_ROI_TLBR_OFFSET = 22 + 160; private static final String TLIN_EN = "TLIN_EN"; private static final int TLIN_EN_OFFSET = 48 + 160; private static final String TLIN_RESOL = "TLIN_RESOL"; private static final int TLIN_RESOL_OFFSET = 49 + 160; private static final String SPOT_AVG_KELV = "SPOT_AVG_KELV"; private static final int SPOT_AVG_KELV_OFFSET = 50 + 160; private static final String SPOT_MAX_KELV = "SPOT_MAX_KELV"; private static final int SPOT_MAX_KELV_OFFSET = 51 + 160; private static final String SPOT_MIN_KELV = "SPOT_MIN_KELV"; private static final int SPOT_MIN_KELV_OFFSET = 52 + 160; private static final String SPOT_POP_PERC = "SPOT_POP_PERC"; private static final int SPOT_POP_PERC_OFFSET = 53 + 160; private static final String SPOT_ROI_TLBR = "SPOT_ROI_TLBR"; private static final int SPOT_ROI_TLBR_OFFSET = 54 + 160; Loading Loading @@ -109,6 +109,17 @@ public class Lepton3Telemetry { this.pixels.length); } } public static int getWidth() { return LEPTON3_IMAGE_WIDTH; } public static int getHeight() { return LEPTON3_IMAGE_HEIGHT; } public static int getTelemetryLines() { return LEPTON3_TELEMETRY_LINES; } public boolean hasTelemetry() { return bbtm != null; Loading Loading
src/main/java/com/elphel/imagej/readers/Boson640Telemetry.java 0 → 100644 +192 −0 Original line number Diff line number Diff line /** ** -----------------------------------------------------------------------------** ** Boson640Telemetry.java ** ** Parses FLIR Boson640 telemetry data ** ** Copyright (C) 2021 Elphel, Inc. ** ** -----------------------------------------------------------------------------** ** ** Boson640Telemetry.java is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see <http://www.gnu.org/licenses/>. ** -----------------------------------------------------------------------------** ** */ package com.elphel.imagej.readers; import java.nio.ByteBuffer; import java.util.HashMap; public class Boson640Telemetry { private static final int BOSON640_IMAGE_WIDTH = 640; private static final int BOSON640_IMAGE_HEIGHT = 512; private static final int BOSON640_TELEMETRY_LINES = 1; private static final String REVISION = "REVISION"; private static final int REVISION_OFFSET = 0; // 2 private static final String CAMSERIAL = "CAMSERIAL"; private static final int CAMSERIAL_OFFSET = 2; // 4 private static final String SENSSERIAL = "SENSSERIAL"; private static final int SENSSERIAL_OFFSET = 6; // 4 private static final String PART_ASCII = "PART_ASCII"; private static final int PART_ASCII_OFFSET = 10; // 20 private static final String SOFT_REV = "SOFT_REV"; private static final int SOFT_REV_OFFSET = 44; // 12 private static final String FRAME_RATE = "FRAME_RATE"; private static final int FRAME_RATE_OFFSET = 56; // 2 private static final String STATUS = "STATUS"; private static final int STATUS_OFFSET = 76; // hex,8 private static final String FRAME = "FRAME"; private static final int FRAME_OFFSET = 84; // 4 private static final String FRAME_FFC = "FRAME_FFC"; private static final int FRAME_FFC_OFFSET = 88; // 4 private static final String FPA_KELV = "FPA_KELV"; private static final int FPA_KELV_OFFSET = 94; // 2 private static final String FFC_KELV = "FFC_KELV"; private static final int FFC_KELV_OFFSET = 96; // 2 private static final String PIPELINE = "PIPELINE"; private static final int PIPELINE_OFFSET = 110; // 4 private static final String FFC_INTEG = "FFC_INTEG"; private static final int FFC_INTEG_OFFSET = 114; // 2 private static final String NUC_CURRENT = "NUC_CURRENT"; private static final int NUC_CURRENT_OFFSET = 158; // 2 private static final String NUC_DESIRED = "NUC_DESIRED"; private static final int NUC_DESIRED_OFFSET = 160; // 2 private static final String CORE_TEMP = "CORE_TEMP"; private static final int CORE_TEMP_OFFSET = 162; // 4 private static final String OVERTEMP = "OVERTEMP"; private static final int OVERTEMP_OFFSET = 166; // 4 private static final String ROI_POP_LTH = "ROI_POP_LTH"; private static final int ROI_POP_LTH_OFFSET = 170; // 4 private static final String ROI_POP_HTL = "ROI_POP_HTL"; private static final int ROI_POP_HTL_OFFSET = 174; // 4 private static final String TGL_PATT = "TGL_PATT"; private static final int TGL_PATT_OFFSET = 178; // 6 private static final String ZOOM_FACT = "ZOOM_FACT"; private static final int ZOOM_FACT_OFFSET = 184; // 4 private static final String ZOOM_X0 = "ZOOM_X0"; private static final int ZOOM_X0_OFFSET = 188; // 4 private static final String ZOOM_Y0 = "ZOOM_Y0"; private static final int ZOOM_Y0_OFFSET = 192; // 4 private float [] pixels = null; private byte [] btm = null; private ByteBuffer bbtm = null; public Boson640Telemetry(ByteBuffer bb, float [] pixels, boolean bottom) { this.pixels = pixels; int offset = 2*(bottom?(BOSON640_IMAGE_WIDTH * BOSON640_IMAGE_HEIGHT):0); if (pixels.length > BOSON640_IMAGE_WIDTH*BOSON640_IMAGE_HEIGHT) { this.btm = new byte [BOSON640_IMAGE_WIDTH* BOSON640_TELEMETRY_LINES * 2]; bb.position(offset); byte dbg = bb.get(); dbg = bb.get(); dbg = bb.get(); dbg = bb.get(); dbg = bb.get(); bb.position(offset); // this.bbtm = bb.get(btm, 0,btm.length); this.bbtm = ByteBuffer.wrap(btm); this.bbtm.order( bb.order()); // or is it already same as in bb? System.out.println("getShort(94)"+this.bbtm.getShort(94)); System.out.println("getLong(162)"+this.bbtm.getLong(162)); System.out.println("getLong(162)"+this.bbtm.getInt(162)); this.pixels = new float [BOSON640_IMAGE_WIDTH * BOSON640_IMAGE_HEIGHT]; System.arraycopy( pixels, (bottom? 0: BOSON640_IMAGE_WIDTH * BOSON640_TELEMETRY_LINES), this.pixels, 0, this.pixels.length); } } public static int getWidth() { return BOSON640_IMAGE_WIDTH; } public static int getHeight() { return BOSON640_IMAGE_HEIGHT; } public static int getTelemetryLines() { return BOSON640_TELEMETRY_LINES; } public boolean hasTelemetry() { return bbtm != null; } public float [] getPixels() { return pixels; } public HashMap<String, String> parseTelemetry(){ HashMap<String, String> tm = new HashMap<String, String>(); byte [] bytes = getBytes(REVISION_OFFSET,2); tm.put(REVISION, "" + bytes[0] + "." + bytes[1]); // "0.2" tm.put(CAMSERIAL, "" + String.format("%d", getU32 (CAMSERIAL_OFFSET))); tm.put(SENSSERIAL, "" + String.format("%d", getU32 (SENSSERIAL_OFFSET))); tm.put(PART_ASCII, "" + getAscii(PART_ASCII_OFFSET,20)); tm.put(STATUS, "" + (bbtm.getLong(STATUS_OFFSET))); // getHex (STATUS_OFFSET,8)); tm.put(SOFT_REV, "" + getU32 (SOFT_REV_OFFSET)+"."+getU32 (SOFT_REV_OFFSET+4)+"."+getU32 (SOFT_REV_OFFSET+8)); tm.put(FRAME_RATE, "" + getU16 (FRAME_RATE_OFFSET)); tm.put(FRAME, "" + getU32 (FRAME_OFFSET)); tm.put(FRAME_FFC, "" + getU32 (FRAME_FFC_OFFSET)); tm.put(FPA_KELV, String.format("%.1f", getKelv10 (FPA_KELV_OFFSET))); tm.put(FFC_KELV, String.format("%.1f", getKelv10 (FFC_KELV_OFFSET))); tm.put(PIPELINE, "" + getU32 (PIPELINE_OFFSET)); tm.put(FFC_INTEG, "" + getU16 (FFC_INTEG_OFFSET)); tm.put(NUC_CURRENT, "" + getU16 (NUC_CURRENT_OFFSET)); tm.put(NUC_DESIRED, "" + getU16 (NUC_DESIRED_OFFSET)); tm.put(CORE_TEMP, String.format("%.3f", getCelsius1000 (CORE_TEMP_OFFSET))); tm.put(OVERTEMP, "" + getU32 (OVERTEMP_OFFSET)); tm.put(ROI_POP_LTH, "" + getU32 (ROI_POP_LTH_OFFSET)); tm.put(ROI_POP_HTL, "" + getU32 (ROI_POP_HTL_OFFSET)); tm.put(TGL_PATT, "" + getHex (TGL_PATT_OFFSET,8)); tm.put(ZOOM_FACT, "" + getU32 (ZOOM_FACT_OFFSET)); tm.put(ZOOM_X0, "" + getU32 (ZOOM_X0_OFFSET)); tm.put(ZOOM_Y0, "" + getU32 (ZOOM_Y0_OFFSET)); return tm; } // internal methods private long getU32(int offset) { return ((long) (bbtm.getInt(offset))) & 0xffffffff; } private long getU16(int offset) { return ((long) (bbtm.getShort(offset))) & 0xffff; } private byte[] getBytes (int offset, int len) { // offset, len in bytes! byte [] bytes = new byte [len]; for (int i = 0; i<len; i++) { bytes[i] = bbtm.get(offset+i); } return bytes; } private String getHex (int offset, int len) { // offset, len bytes, String s = ""; for (byte b :getBytes(offset, len)) { s += String.format("%02x", b); } return s; } private String getAscii (int offset, int len) { // offset, len bytes, String s = ""; for (byte b :getBytes(offset, len)) { if (b == 0) break; s += String.format("%c", b); } return s; } private double getKelv10(int offset) { return 0.1*getU16(offset); } private double getCelsius1000(int offset) { return 0.001*bbtm.getInt(offset); } }
src/main/java/com/elphel/imagej/readers/ImagejJp4Tiff.java +41 −6 Original line number Diff line number Diff line Loading @@ -206,24 +206,49 @@ public class ImagejJp4Tiff { Hashtable<String, Object> meta_hash = reader.getGlobalMetadata(); boolean degamma = bytes_per_pixel < 2; // both JP4 and 8-bit tiff // FIXME: Do not scale telemetry! /* if (deGammaScale(pixels, reader.getSizeX(), meta_hash, degamma, scale ) == null) { LOGGER.error("Problem degamma/scaling of "+content_fileName); } */ boolean telemetry = (bytes_per_pixel == 2); boolean lepton_telemetry = telemetry && ((reader.getSizeX() == 160)); boolean boson_telemetry = telemetry && ((reader.getSizeX() == 640)); Lepton3Telemetry lepton3Telemetry = null; Boson640Telemetry boson640Telemetry = null; if (telemetry) { if (lepton_telemetry) { lepton3Telemetry = new Lepton3Telemetry(bb,pixels, true); // bottom telemetry = lepton3Telemetry.hasTelemetry(); } else if (boson_telemetry) { boson640Telemetry = new Boson640Telemetry(bb,pixels, false); // top telemetry = boson640Telemetry.hasTelemetry(); } } ImageProcessor ip = null; if (telemetry) { ip=new FloatProcessor(Lepton3Telemetry.LEPTON3_IMAGE_WIDTH, Lepton3Telemetry.LEPTON3_IMAGE_HEIGHT); ip.setPixels(lepton3Telemetry.getPixels()); // bo if (lepton_telemetry) { ip=new FloatProcessor(Lepton3Telemetry.getWidth(), Lepton3Telemetry.getHeight()); pixels = lepton3Telemetry.getPixels(); // ip.setPixels(lepton3Telemetry.getPixels()); // bo } else if (boson_telemetry) { ip=new FloatProcessor(Boson640Telemetry.getWidth(), Boson640Telemetry.getHeight()); pixels=boson640Telemetry.getPixels(); // bo // ip.setPixels(boson640Telemetry.getPixels()); // bo } } else { ip=new FloatProcessor(reader.getSizeX(), reader.getSizeY()); ip.setPixels(pixels); // ip.setPixels(pixels); } if (deGammaScale(pixels, reader.getSizeX(), meta_hash, degamma, scale ) == null) { LOGGER.error("Problem degamma/scaling of "+content_fileName); } ip.setPixels(pixels); ip.resetMinAndMax(); String prefix = ElphelTiffReader.ELPHEL_PROPERTY_PREFIX; String imageName = content_fileName; // path; Loading @@ -247,12 +272,18 @@ public class ImagejJp4Tiff { imp.setProperty(std+(key.replace(" ","_").replace("/","_")), meta_hash.get(key).toString()); } } if (telemetry) { if (lepton_telemetry) { HashMap<String, String> telemetryMap = lepton3Telemetry.parseTelemetry(); for (String key:telemetryMap.keySet()) { imp.setProperty(TELEMETRY_PREFIX+key, telemetryMap.get(key)); } } if (boson_telemetry) { HashMap<String, String> telemetryMap = boson640Telemetry.parseTelemetry(); for (String key:telemetryMap.keySet()) { imp.setProperty(TELEMETRY_PREFIX+key, telemetryMap.get(key)); } } encodeProperiesToInfo(imp); Location.mapFile(content_fileName, null); return imp; Loading Loading @@ -368,6 +399,10 @@ public class ImagejJp4Tiff { for (int x = 0; x < width; x+=2) { for (int dx = 0; dx<2; dx++) { int indx = base + x + dx; if (indx >= pixels.length) { System.out.println(indx); continue; } pixels[indx] *= rgains[(dy << 1) + 1 - dx]; } } Loading
src/main/java/com/elphel/imagej/readers/Lepton3Telemetry.java +58 −47 Original line number Diff line number Diff line Loading @@ -29,53 +29,53 @@ import java.nio.ByteBuffer; import java.util.HashMap; public class Lepton3Telemetry { public static final int LEPTON3_IMAGE_WIDTH = 160; public static final int LEPTON3_IMAGE_HEIGHT = 120; public static final int LEPTON3_TELEMETRY_LINES = 2; public static final String REVISION = "REVISION"; public static final int REVISION_OFFSET = 0; public static final String UPTIME = "UPTIME"; public static final int UPTIME_OFFSET = 1; public static final String STATUS = "STATUS"; public static final int STATUS_OFFSET = 3; public static final String MOD_SER = "MOD_SER"; public static final int MOD_SER_OFFSET = 5; public static final String SOFT_REV = "SOFT_REV"; public static final int SOFT_REV_OFFSET = 13; public static final String FRAME = "FRAME"; public static final int FRAME_OFFSET = 20; public static final String MEAN = "MEAN"; public static final int MEAN_OFFSET = 22; public static final String FPA_TCNT = "FPA_TCNT"; public static final int FPA_TCNT_OFFSET = 23; public static final String FPA_KELV = "FPA_KELV"; public static final int FPA_KELV_OFFSET = 24; public static final String ENCL_TCNT = "ENCL_TCNT"; public static final int ENCL_TCNT_OFFSET = 25; public static final String ENCL_KELV = "ENCL_KELV"; public static final int ENCL_KELV_OFFSET = 26; public static final String FFC_KELV = "FFC_KELV"; public static final int FFC_KELV_OFFSET = 29; public static final String FFC_TIME = "FFC_TIME"; public static final int FFC_TIME_OFFSET = 30; public static final String ENCL_FFC_KELV = "ENCL_FFC_KELV"; public static final int ENCL_FFC_KELV_OFFSET = 32; public static final String AGC_ROI_TLBR = "AGC_ROI_TLBR"; public static final int AGC_ROI_TLBR_OFFSET = 34; public static final String AGC_CLIP_HIGH = "AGC_CLIP_HIGH"; public static final int AGC_CLIP_HIGH_OFFSET = 38; public static final String AGC_CLIP_LOW = "AGC_CLIP_LOW"; public static final int AGC_CLIP_LOW_OFFSET = 38; public static final String VFORMAT = "VFORMAT"; public static final int VFORMAT_OFFSET = 72; public static final String FFC_LOG2 = "FFC_LOG2"; public static final int FFC_LOG2_OFFSET = 74; public static final String EMISS_8192 = "EMISS_8192"; public static final int EMISS_8192_OFFSET = 19 + 80; // row B public static final String BGND_KELV = "BGND_KELV"; public static final int BGND_KELV_OFFSET = 20 + 80; public static final String ATMOSPH_8192 = "ATMOSPH_8192"; public static final int ATMOSPH_8192_OFFSET = 21 + 80; public static final String ATMOSPH_KELV = "ATMOSPH_KELV"; public static final int ATMOSPH_KELV_OFFSET = 22 + 80; public static final String WND_TRANS_8192 ="WND_TRANS_8192";public static final int WND_TRANS_8192_OFFSET =23 + 80; public static final String WND_REFL_8192 = "WND_REFL_8192"; public static final int WND_REFL_8192_OFFSET = 24 + 80; public static final String WND_KELV = "WND_KELV"; public static final int WND_KELV_OFFSET = 25 + 80; public static final String WND_REFL_KELV = "WND_REFL_KELV"; public static final int WND_REFL_KELV_OFFSET = 26 + 80; public static final String GAIN_MODE = "GAIN_MODE"; public static final int GAIN_MODE_OFFSET = 5 + 160; // row C public static final String GAIN_EFF = "GAIN_EFF"; public static final int GAIN_EFF_OFFSET = 6 + 160; public static final String GAIN_DFLAG = "GAIN_DFLAG"; public static final int GAIN_DFLAG_OFFSET = 7 + 160; public static final String GAIN_THR_HL_C = "GAIN_THR_HL_C"; public static final int GAIN_THR_HL_C_OFFSET = 8 + 160; public static final String GAIN_THR_LH_C = "GAIN_THR_LH_C"; public static final int GAIN_THR_LH_C_OFFSET = 9 + 160; public static final String GAIN_THR_HL_K = "GAIN_THR_HL_K"; public static final int GAIN_THR_HL_K_OFFSET = 10 + 160; public static final String GAIN_THR_LH_K = "GAIN_THR_LH_K"; public static final int GAIN_THR_LH_K_OFFSET = 11 + 160; public static final String GAIN_HL_PP = "GAIN_HL_PP"; public static final int GAIN_HL_PP_OFFSET = 14 + 160; public static final String GAIN_LH_PP = "GAIN_LH_PP"; public static final int GAIN_LH_PP_OFFSET = 15 + 160; public static final String GAIN_ROI_TLBR = "GAIN_ROI_TLBR"; public static final int GAIN_ROI_TLBR_OFFSET = 22 + 160; public static final String TLIN_EN = "TLIN_EN"; public static final int TLIN_EN_OFFSET = 48 + 160; public static final String TLIN_RESOL = "TLIN_RESOL"; public static final int TLIN_RESOL_OFFSET = 49 + 160; public static final String SPOT_AVG_KELV = "SPOT_AVG_KELV"; public static final int SPOT_AVG_KELV_OFFSET = 50 + 160; public static final String SPOT_MAX_KELV = "SPOT_MAX_KELV"; public static final int SPOT_MAX_KELV_OFFSET = 51 + 160; public static final String SPOT_MIN_KELV = "SPOT_MIN_KELV"; public static final int SPOT_MIN_KELV_OFFSET = 52 + 160; public static final String SPOT_POP_PERC = "SPOT_POP_PERC"; public static final int SPOT_POP_PERC_OFFSET = 53 + 160; public static final String SPOT_ROI_TLBR = "SPOT_ROI_TLBR"; public static final int SPOT_ROI_TLBR_OFFSET = 54 + 160; private static final int LEPTON3_IMAGE_WIDTH = 160; private static final int LEPTON3_IMAGE_HEIGHT = 120; private static final int LEPTON3_TELEMETRY_LINES = 2; private static final String REVISION = "REVISION"; private static final int REVISION_OFFSET = 0; private static final String UPTIME = "UPTIME"; private static final int UPTIME_OFFSET = 1; private static final String STATUS = "STATUS"; private static final int STATUS_OFFSET = 3; private static final String MOD_SER = "MOD_SER"; private static final int MOD_SER_OFFSET = 5; private static final String SOFT_REV = "SOFT_REV"; private static final int SOFT_REV_OFFSET = 13; private static final String FRAME = "FRAME"; private static final int FRAME_OFFSET = 20; private static final String MEAN = "MEAN"; private static final int MEAN_OFFSET = 22; private static final String FPA_TCNT = "FPA_TCNT"; private static final int FPA_TCNT_OFFSET = 23; private static final String FPA_KELV = "FPA_KELV"; private static final int FPA_KELV_OFFSET = 24; private static final String ENCL_TCNT = "ENCL_TCNT"; private static final int ENCL_TCNT_OFFSET = 25; private static final String ENCL_KELV = "ENCL_KELV"; private static final int ENCL_KELV_OFFSET = 26; private static final String FFC_KELV = "FFC_KELV"; private static final int FFC_KELV_OFFSET = 29; private static final String FFC_TIME = "FFC_TIME"; private static final int FFC_TIME_OFFSET = 30; private static final String ENCL_FFC_KELV = "ENCL_FFC_KELV"; private static final int ENCL_FFC_KELV_OFFSET = 32; private static final String AGC_ROI_TLBR = "AGC_ROI_TLBR"; private static final int AGC_ROI_TLBR_OFFSET = 34; private static final String AGC_CLIP_HIGH = "AGC_CLIP_HIGH"; private static final int AGC_CLIP_HIGH_OFFSET = 38; private static final String AGC_CLIP_LOW = "AGC_CLIP_LOW"; private static final int AGC_CLIP_LOW_OFFSET = 38; private static final String VFORMAT = "VFORMAT"; private static final int VFORMAT_OFFSET = 72; private static final String FFC_LOG2 = "FFC_LOG2"; private static final int FFC_LOG2_OFFSET = 74; private static final String EMISS_8192 = "EMISS_8192"; private static final int EMISS_8192_OFFSET = 19 + 80; // row B private static final String BGND_KELV = "BGND_KELV"; private static final int BGND_KELV_OFFSET = 20 + 80; private static final String ATMOSPH_8192 = "ATMOSPH_8192"; private static final int ATMOSPH_8192_OFFSET = 21 + 80; private static final String ATMOSPH_KELV = "ATMOSPH_KELV"; private static final int ATMOSPH_KELV_OFFSET = 22 + 80; private static final String WND_TRANS_8192 ="WND_TRANS_8192";private static final int WND_TRANS_8192_OFFSET =23 + 80; private static final String WND_REFL_8192 = "WND_REFL_8192"; private static final int WND_REFL_8192_OFFSET = 24 + 80; private static final String WND_KELV = "WND_KELV"; private static final int WND_KELV_OFFSET = 25 + 80; private static final String WND_REFL_KELV = "WND_REFL_KELV"; private static final int WND_REFL_KELV_OFFSET = 26 + 80; private static final String GAIN_MODE = "GAIN_MODE"; private static final int GAIN_MODE_OFFSET = 5 + 160; // row C private static final String GAIN_EFF = "GAIN_EFF"; private static final int GAIN_EFF_OFFSET = 6 + 160; private static final String GAIN_DFLAG = "GAIN_DFLAG"; private static final int GAIN_DFLAG_OFFSET = 7 + 160; private static final String GAIN_THR_HL_C = "GAIN_THR_HL_C"; private static final int GAIN_THR_HL_C_OFFSET = 8 + 160; private static final String GAIN_THR_LH_C = "GAIN_THR_LH_C"; private static final int GAIN_THR_LH_C_OFFSET = 9 + 160; private static final String GAIN_THR_HL_K = "GAIN_THR_HL_K"; private static final int GAIN_THR_HL_K_OFFSET = 10 + 160; private static final String GAIN_THR_LH_K = "GAIN_THR_LH_K"; private static final int GAIN_THR_LH_K_OFFSET = 11 + 160; private static final String GAIN_HL_PP = "GAIN_HL_PP"; private static final int GAIN_HL_PP_OFFSET = 14 + 160; private static final String GAIN_LH_PP = "GAIN_LH_PP"; private static final int GAIN_LH_PP_OFFSET = 15 + 160; private static final String GAIN_ROI_TLBR = "GAIN_ROI_TLBR"; private static final int GAIN_ROI_TLBR_OFFSET = 22 + 160; private static final String TLIN_EN = "TLIN_EN"; private static final int TLIN_EN_OFFSET = 48 + 160; private static final String TLIN_RESOL = "TLIN_RESOL"; private static final int TLIN_RESOL_OFFSET = 49 + 160; private static final String SPOT_AVG_KELV = "SPOT_AVG_KELV"; private static final int SPOT_AVG_KELV_OFFSET = 50 + 160; private static final String SPOT_MAX_KELV = "SPOT_MAX_KELV"; private static final int SPOT_MAX_KELV_OFFSET = 51 + 160; private static final String SPOT_MIN_KELV = "SPOT_MIN_KELV"; private static final int SPOT_MIN_KELV_OFFSET = 52 + 160; private static final String SPOT_POP_PERC = "SPOT_POP_PERC"; private static final int SPOT_POP_PERC_OFFSET = 53 + 160; private static final String SPOT_ROI_TLBR = "SPOT_ROI_TLBR"; private static final int SPOT_ROI_TLBR_OFFSET = 54 + 160; Loading Loading @@ -109,6 +109,17 @@ public class Lepton3Telemetry { this.pixels.length); } } public static int getWidth() { return LEPTON3_IMAGE_WIDTH; } public static int getHeight() { return LEPTON3_IMAGE_HEIGHT; } public static int getTelemetryLines() { return LEPTON3_TELEMETRY_LINES; } public boolean hasTelemetry() { return bbtm != null; Loading