Skip to content

Commit

Permalink
recover jpeg header in stratosattk1 image decoder
Browse files Browse the repository at this point in the history
  • Loading branch information
dernasherbrezon committed Jan 9, 2024
1 parent 894d3a3 commit a960250
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package ru.r2cloud.jradio.sstk1;

import java.util.Comparator;

public class StratosatTk1BeaconComparator implements Comparator<StratosatTk1Beacon> {

public static final StratosatTk1BeaconComparator INSTANCE = new StratosatTk1BeaconComparator();

@Override
public int compare(StratosatTk1Beacon o1, StratosatTk1Beacon o2) {
if (o1.getFileData() == null || o2.getFileData() == null) {
// doesn't matter because we're interested only in the beacons with file data
return Long.compare(o1.getBeginSample(), o2.getBeginSample());
}
return Integer.compare(o1.getTk1Header().getPacketOffset(), o2.getTk1Header().getPacketOffset());
}

}
100 changes: 89 additions & 11 deletions src/main/java/ru/r2cloud/jradio/sstk1/StratosatTk1PictureDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
Expand All @@ -20,21 +21,77 @@ public class StratosatTk1PictureDecoder implements Iterator<BufferedImage> {
private List<StratosatTk1Beacon> currentBatch;
private int currentIndex = 0;

// TODO jpeg header for 0509 type
// jpeg header for 0509 type
// can be used to recover some missing messages
// 02003E0509000004FFD8FFDB0084000D09090B0A080D0B0A0B0E0E0D0F13201513121213271C1E17202E2931302E292D2C333A4A3E333646372C2D405741464C58E5
// 02003E05093800044E525352323E5A615A50604A51524F010E0E0E131113261515264F352D354F4F4F4F4F4F4F4F4F4F4F4F4F4F4F4F4F4F4F4F4F4F4F4F4F4F670D
// 02003E05097000044F4F4F4F4F4F4F4F4F4F4F4F4F4F4F4F4F4F4F4F4F4F4F4FFFC401A20000010501010101010100000000000000000102030405060708090A5D56
// 02003E0509A800040B100002010303020403050504040000017D01020300041105122131410613516107227114328191A1082342B1C11552D1F024336272820916C1
// 02003E0509E000040A161718191A25262728292A3435363738393A434445464748494A535455565758595A636465666768696A737475767778797A83848586878F2D
// 02003E050918010488898A92939495969798999AA2A3A4A5A6A7A8A9AAB2B3B4B5B6B7B8B9BAC2C3C4C5C6C7C8C9CAD2D3D4D5D6D7D8D9DAE1E2E3E4E5E6E7E8711A
// 02003E0509500104E9EAF1F2F3F4F5F6F7F8F9FA0100030101010101010101010000000000000102030405060708090A0B11000201020404030407050404000117FB
// 02003E05098801040277000102031104052131061241510761711322328108144291A1B1C109233352F0156272D10A162434E125F11718191A262728292A35364948
// 02003E0509C001043738393A434445464748494A535455565758595A636465666768696A737475767778797A82838485868788898A92939495969798999AA2A397A2
// 02003E0509F80104A4A5A6A7A8A9AAB2B3B4B5B6B7B8B9BAC2C3C4C5C6C7C8C9CAD2D3D4D5D6D7D8D9DAE2E3E4E5E6E7E8E9EAF2F3F4F5F6F7F8F9FAFFC000111F66
private static final List<StratosatTk1Beacon> jpegHeader = new ArrayList<>();

static {
jpegHeader.add(createFromBytes(new byte[] { (byte) 0x02, (byte) 0x00, (byte) 0x3E, (byte) 0x05, (byte) 0x09, (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0xFF, (byte) 0xD8, (byte) 0xFF, (byte) 0xDB, (byte) 0x00, (byte) 0x84, (byte) 0x00, (byte) 0x0D, (byte) 0x09, (byte) 0x09, (byte) 0x0B,
(byte) 0x0A, (byte) 0x08, (byte) 0x0D, (byte) 0x0B, (byte) 0x0A, (byte) 0x0B, (byte) 0x0E, (byte) 0x0E, (byte) 0x0D, (byte) 0x0F, (byte) 0x13, (byte) 0x20, (byte) 0x15, (byte) 0x13, (byte) 0x12, (byte) 0x12, (byte) 0x13, (byte) 0x27, (byte) 0x1C, (byte) 0x1E, (byte) 0x17,
(byte) 0x20, (byte) 0x2E, (byte) 0x29, (byte) 0x31, (byte) 0x30, (byte) 0x2E, (byte) 0x29, (byte) 0x2D, (byte) 0x2C, (byte) 0x33, (byte) 0x3A, (byte) 0x4A, (byte) 0x3E, (byte) 0x33, (byte) 0x36, (byte) 0x46, (byte) 0x37, (byte) 0x2C, (byte) 0x2D, (byte) 0x40, (byte) 0x57,
(byte) 0x41, (byte) 0x46, (byte) 0x4C, (byte) 0x58, (byte) 0xE5 }));
jpegHeader.add(createFromBytes(new byte[] { (byte) 0x02, (byte) 0x00, (byte) 0x3E, (byte) 0x05, (byte) 0x09, (byte) 0x38, (byte) 0x00, (byte) 0x04, (byte) 0x4E, (byte) 0x52, (byte) 0x53, (byte) 0x52, (byte) 0x32, (byte) 0x3E, (byte) 0x5A, (byte) 0x61, (byte) 0x5A, (byte) 0x50, (byte) 0x60,
(byte) 0x4A, (byte) 0x51, (byte) 0x52, (byte) 0x4F, (byte) 0x01, (byte) 0x0E, (byte) 0x0E, (byte) 0x0E, (byte) 0x13, (byte) 0x11, (byte) 0x13, (byte) 0x26, (byte) 0x15, (byte) 0x15, (byte) 0x26, (byte) 0x4F, (byte) 0x35, (byte) 0x2D, (byte) 0x35, (byte) 0x4F, (byte) 0x4F,
(byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F,
(byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x67, (byte) 0x0D }));
jpegHeader.add(createFromBytes(new byte[] { (byte) 0x02, (byte) 0x00, (byte) 0x3E, (byte) 0x05, (byte) 0x09, (byte) 0x70, (byte) 0x00, (byte) 0x04, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F,
(byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0x4F, (byte) 0xFF, (byte) 0xC4, (byte) 0x01, (byte) 0xA2, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x05,
(byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07,
(byte) 0x08, (byte) 0x09, (byte) 0x0A, (byte) 0x5D, (byte) 0x56 }));
jpegHeader.add(createFromBytes(new byte[] { (byte) 0x02, (byte) 0x00, (byte) 0x3E, (byte) 0x05, (byte) 0x09, (byte) 0xA8, (byte) 0x00, (byte) 0x04, (byte) 0x0B, (byte) 0x10, (byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x03, (byte) 0x03, (byte) 0x02, (byte) 0x04, (byte) 0x03, (byte) 0x05,
(byte) 0x05, (byte) 0x04, (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x7D, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x00, (byte) 0x04, (byte) 0x11, (byte) 0x05, (byte) 0x12, (byte) 0x21, (byte) 0x31, (byte) 0x41, (byte) 0x06, (byte) 0x13, (byte) 0x51,
(byte) 0x61, (byte) 0x07, (byte) 0x22, (byte) 0x71, (byte) 0x14, (byte) 0x32, (byte) 0x81, (byte) 0x91, (byte) 0xA1, (byte) 0x08, (byte) 0x23, (byte) 0x42, (byte) 0xB1, (byte) 0xC1, (byte) 0x15, (byte) 0x52, (byte) 0xD1, (byte) 0xF0, (byte) 0x24, (byte) 0x33, (byte) 0x62,
(byte) 0x72, (byte) 0x82, (byte) 0x09, (byte) 0x16, (byte) 0xC1 }));
jpegHeader.add(createFromBytes(new byte[] { (byte) 0x02, (byte) 0x00, (byte) 0x3E, (byte) 0x05, (byte) 0x09, (byte) 0xE0, (byte) 0x00, (byte) 0x04, (byte) 0x0A, (byte) 0x16, (byte) 0x17, (byte) 0x18, (byte) 0x19, (byte) 0x1A, (byte) 0x25, (byte) 0x26, (byte) 0x27, (byte) 0x28, (byte) 0x29,
(byte) 0x2A, (byte) 0x34, (byte) 0x35, (byte) 0x36, (byte) 0x37, (byte) 0x38, (byte) 0x39, (byte) 0x3A, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47, (byte) 0x48, (byte) 0x49, (byte) 0x4A, (byte) 0x53, (byte) 0x54, (byte) 0x55, (byte) 0x56, (byte) 0x57,
(byte) 0x58, (byte) 0x59, (byte) 0x5A, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67, (byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77, (byte) 0x78, (byte) 0x79, (byte) 0x7A, (byte) 0x83, (byte) 0x84,
(byte) 0x85, (byte) 0x86, (byte) 0x87, (byte) 0x8F, (byte) 0x2D }));
jpegHeader.add(createFromBytes(new byte[] { (byte) 0x02, (byte) 0x00, (byte) 0x3E, (byte) 0x05, (byte) 0x09, (byte) 0x18, (byte) 0x01, (byte) 0x04, (byte) 0x88, (byte) 0x89, (byte) 0x8A, (byte) 0x92, (byte) 0x93, (byte) 0x94, (byte) 0x95, (byte) 0x96, (byte) 0x97, (byte) 0x98, (byte) 0x99,
(byte) 0x9A, (byte) 0xA2, (byte) 0xA3, (byte) 0xA4, (byte) 0xA5, (byte) 0xA6, (byte) 0xA7, (byte) 0xA8, (byte) 0xA9, (byte) 0xAA, (byte) 0xB2, (byte) 0xB3, (byte) 0xB4, (byte) 0xB5, (byte) 0xB6, (byte) 0xB7, (byte) 0xB8, (byte) 0xB9, (byte) 0xBA, (byte) 0xC2, (byte) 0xC3,
(byte) 0xC4, (byte) 0xC5, (byte) 0xC6, (byte) 0xC7, (byte) 0xC8, (byte) 0xC9, (byte) 0xCA, (byte) 0xD2, (byte) 0xD3, (byte) 0xD4, (byte) 0xD5, (byte) 0xD6, (byte) 0xD7, (byte) 0xD8, (byte) 0xD9, (byte) 0xDA, (byte) 0xE1, (byte) 0xE2, (byte) 0xE3, (byte) 0xE4, (byte) 0xE5,
(byte) 0xE6, (byte) 0xE7, (byte) 0xE8, (byte) 0x71, (byte) 0x1A }));
jpegHeader.add(createFromBytes(new byte[] { (byte) 0x02, (byte) 0x00, (byte) 0x3E, (byte) 0x05, (byte) 0x09, (byte) 0x50, (byte) 0x01, (byte) 0x04, (byte) 0xE9, (byte) 0xEA, (byte) 0xF1, (byte) 0xF2, (byte) 0xF3, (byte) 0xF4, (byte) 0xF5, (byte) 0xF6, (byte) 0xF7, (byte) 0xF8, (byte) 0xF9,
(byte) 0xFA, (byte) 0x01, (byte) 0x00, (byte) 0x03, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x02,
(byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08, (byte) 0x09, (byte) 0x0A, (byte) 0x0B, (byte) 0x11, (byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x02, (byte) 0x04, (byte) 0x04, (byte) 0x03, (byte) 0x04, (byte) 0x07, (byte) 0x05, (byte) 0x04,
(byte) 0x04, (byte) 0x00, (byte) 0x01, (byte) 0x17, (byte) 0xFB }));
jpegHeader.add(createFromBytes(new byte[] { (byte) 0x02, (byte) 0x00, (byte) 0x3E, (byte) 0x05, (byte) 0x09, (byte) 0x88, (byte) 0x01, (byte) 0x04, (byte) 0x02, (byte) 0x77, (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x11, (byte) 0x04, (byte) 0x05, (byte) 0x21, (byte) 0x31,
(byte) 0x06, (byte) 0x12, (byte) 0x41, (byte) 0x51, (byte) 0x07, (byte) 0x61, (byte) 0x71, (byte) 0x13, (byte) 0x22, (byte) 0x32, (byte) 0x81, (byte) 0x08, (byte) 0x14, (byte) 0x42, (byte) 0x91, (byte) 0xA1, (byte) 0xB1, (byte) 0xC1, (byte) 0x09, (byte) 0x23, (byte) 0x33,
(byte) 0x52, (byte) 0xF0, (byte) 0x15, (byte) 0x62, (byte) 0x72, (byte) 0xD1, (byte) 0x0A, (byte) 0x16, (byte) 0x24, (byte) 0x34, (byte) 0xE1, (byte) 0x25, (byte) 0xF1, (byte) 0x17, (byte) 0x18, (byte) 0x19, (byte) 0x1A, (byte) 0x26, (byte) 0x27, (byte) 0x28, (byte) 0x29,
(byte) 0x2A, (byte) 0x35, (byte) 0x36, (byte) 0x49, (byte) 0x48 }));
jpegHeader.add(createFromBytes(new byte[] { (byte) 0x02, (byte) 0x00, (byte) 0x3E, (byte) 0x05, (byte) 0x09, (byte) 0xC0, (byte) 0x01, (byte) 0x04, (byte) 0x37, (byte) 0x38, (byte) 0x39, (byte) 0x3A, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47, (byte) 0x48, (byte) 0x49,
(byte) 0x4A, (byte) 0x53, (byte) 0x54, (byte) 0x55, (byte) 0x56, (byte) 0x57, (byte) 0x58, (byte) 0x59, (byte) 0x5A, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67, (byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76,
(byte) 0x77, (byte) 0x78, (byte) 0x79, (byte) 0x7A, (byte) 0x82, (byte) 0x83, (byte) 0x84, (byte) 0x85, (byte) 0x86, (byte) 0x87, (byte) 0x88, (byte) 0x89, (byte) 0x8A, (byte) 0x92, (byte) 0x93, (byte) 0x94, (byte) 0x95, (byte) 0x96, (byte) 0x97, (byte) 0x98, (byte) 0x99,
(byte) 0x9A, (byte) 0xA2, (byte) 0xA3, (byte) 0x97, (byte) 0xA2 }));
jpegHeader.add(createFromBytes(new byte[] { (byte) 0x02, (byte) 0x00, (byte) 0x3E, (byte) 0x05, (byte) 0x09, (byte) 0xF8, (byte) 0x01, (byte) 0x04, (byte) 0xA4, (byte) 0xA5, (byte) 0xA6, (byte) 0xA7, (byte) 0xA8, (byte) 0xA9, (byte) 0xAA, (byte) 0xB2, (byte) 0xB3, (byte) 0xB4, (byte) 0xB5,
(byte) 0xB6, (byte) 0xB7, (byte) 0xB8, (byte) 0xB9, (byte) 0xBA, (byte) 0xC2, (byte) 0xC3, (byte) 0xC4, (byte) 0xC5, (byte) 0xC6, (byte) 0xC7, (byte) 0xC8, (byte) 0xC9, (byte) 0xCA, (byte) 0xD2, (byte) 0xD3, (byte) 0xD4, (byte) 0xD5, (byte) 0xD6, (byte) 0xD7, (byte) 0xD8,
(byte) 0xD9, (byte) 0xDA, (byte) 0xE2, (byte) 0xE3, (byte) 0xE4, (byte) 0xE5, (byte) 0xE6, (byte) 0xE7, (byte) 0xE8, (byte) 0xE9, (byte) 0xEA, (byte) 0xF2, (byte) 0xF3, (byte) 0xF4, (byte) 0xF5, (byte) 0xF6, (byte) 0xF7, (byte) 0xF8, (byte) 0xF9, (byte) 0xFA, (byte) 0xFF,
(byte) 0xC0, (byte) 0x00, (byte) 0x11, (byte) 0x1F, (byte) 0x66 }));
}

public StratosatTk1PictureDecoder(List<StratosatTk1Beacon> beacons) {
this.beacons = beacons;
Collections.sort(this.beacons, StratosatTk1BeaconComparator.INSTANCE);
if (getMessageType(beacons) == 0x0905) {
// add fake beacons with jpeg header if missing
int currentInputIndex = 0;
for (int i = 0; i < jpegHeader.size();) {
StratosatTk1Beacon fake = jpegHeader.get(i);
StratosatTk1Beacon input = this.beacons.get(currentInputIndex);
if (input.getTk1Header() == null) {
currentInputIndex++;
continue;
}
if (fake.getTk1Header().getPacketOffset() == input.getTk1Header().getPacketOffset()) {
i++;
currentInputIndex++;
continue;
}
this.beacons.add(fake);
i++;
}
}
// sort one more time to make sure all fake beacons are first
Collections.sort(this.beacons, StratosatTk1BeaconComparator.INSTANCE);
}

@Override
Expand Down Expand Up @@ -90,4 +147,25 @@ public BufferedImage next() {
}
}

private static int getMessageType(List<StratosatTk1Beacon> beacons) {
for (StratosatTk1Beacon cur : beacons) {
if (cur.getTk1Header() == null) {
continue;
}
return cur.getTk1Header().getMessageType();
}
// doesn't matter. message types are always non zero
return 0;
}

private static StratosatTk1Beacon createFromBytes(byte[] data) {
StratosatTk1Beacon result = new StratosatTk1Beacon();
try {
result.readExternal(data);
} catch (Exception e) {
throw new IllegalArgumentException("can't parse beacon");
}
return result;
}

}

0 comments on commit a960250

Please sign in to comment.