19 package org.sleuthkit.autopsy.modules.drones;
21 import java.io.BufferedReader;
23 import java.io.FileReader;
24 import java.io.IOException;
25 import java.nio.file.Paths;
26 import java.time.ZoneOffset;
27 import java.time.ZonedDateTime;
28 import java.time.format.DateTimeFormatter;
29 import java.time.format.DateTimeParseException;
30 import java.util.ArrayList;
31 import java.util.HashMap;
32 import java.util.List;
34 import java.util.logging.Level;
35 import java.util.logging.Logger;
36 import org.openide.util.NbBundle;
37 import org.openide.util.NbBundle.Messages;
44 import org.
sleuthkit.datamodel.blackboardutils.GeoArtifactsHelper;
46 import org.
sleuthkit.datamodel.Blackboard.BlackboardException;
47 import org.
sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints;
48 import org.
sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints.TrackPoint;
57 final class DATExtractor
extends DroneExtractor {
59 private static final Logger logger = Logger.getLogger(DATExtractor.class.getName());
61 private static final String HEADER_LONG =
"IMU_ATTI(0):Longitude";
62 private static final String HEADER_LAT =
"IMU_ATTI(0):Latitude";
63 private static final String HEADER_VELOCITY =
"IMU_ATTI(0):velComposite";
64 private static final String HEADER_DATETILE =
"GPS:dateTimeStamp";
65 private static final String HEADER_ALTITUDE =
"GPS(0):heightMSL";
66 private static final String HEADER_DISTANCE_FROM_HP =
"IMU_ATTI(0):distanceHP";
67 private static final String HEADER_DISTANCE_TRAVELED =
"IMU_ATTI(0):distanceTravelled";
74 DATExtractor() throws DroneIngestException {
79 "DATExtractor_process_message=Processing DJI DAT file: %s"
82 void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar)
throws DroneIngestException {
83 List<AbstractFile> datFiles = findDATFiles(dataSource);
85 DATDumper dumper =
new DATDumper();
88 for (AbstractFile DATFile : datFiles) {
89 if (context.dataSourceIngestIsCancelled()) {
93 progressBar.progress(String.format(Bundle.DATExtractor_process_message(), DATFile.getName()));
96 File tempDATFile = getTemporaryFile(context, DATFile);
99 String csvFilePath = getCSVPathForDAT(DATFile);
102 if (!dumper.isDATFile(tempDATFile.getAbsolutePath())) {
103 logger.log(Level.WARNING, String.format(
"%s is not a valid DAT file", DATFile.getName()));
107 dumper.dumpDATFile(tempDATFile.getAbsolutePath(), csvFilePath,
true);
109 if (context.dataSourceIngestIsCancelled()) {
114 GeoTrackPoints trackPoints = processCSVFile(context, DATFile, csvFilePath);
116 if (trackPoints != null && !trackPoints.isEmpty()) {
117 (
new GeoArtifactsHelper(getSleuthkitCase(), getName(),
"DatCon", DATFile)).addTrack(DATFile.getName(), trackPoints, null);
119 logger.log(Level.INFO, String.format(
"No trackpoints with valid longitude or latitude found in %s", DATFile.getName()));
122 }
catch (TskCoreException | BlackboardException ex) {
123 logger.log(Level.WARNING, String.format(
"Exception thrown while processing DAT file %s", DATFile.getName()), ex);
125 tempDATFile.delete();
126 (
new File(csvFilePath)).
delete();
130 FileUtil.deleteDir(getExtractorTempPath().toFile());
135 "DATFileExtractor_Extractor_Name=DAT File Extractor"
140 return Bundle.DATFileExtractor_Extractor_Name();
153 private List<AbstractFile> findDATFiles(Content dataSource)
throws DroneIngestException {
154 List<AbstractFile> fileList =
new ArrayList<>();
156 FileManager fileManager = getCurrentCase().getServices().getFileManager();
160 fileList = fileManager.findFiles(dataSource,
"FLY___.DAT");
161 }
catch (TskCoreException ex) {
162 throw new DroneIngestException(
"Unable to find drone DAT files.", ex);
175 private String getCSVPathForDAT(AbstractFile file) {
176 String tempFileName = file.getName() + file.getId() +
".csv";
177 return Paths.get(getExtractorTempPath().toString(), tempFileName).toString();
191 private GeoTrackPoints processCSVFile(IngestJobContext context, AbstractFile DATFile, String csvFilePath)
throws DroneIngestException {
192 GeoTrackPoints trackPoints =
new GeoTrackPoints();
193 try (BufferedReader reader =
new BufferedReader(
new FileReader(
new File(csvFilePath)))) {
195 String line = reader.readLine();
196 Map<String, Integer> headerMap = makeHeaderMap(line.split(
","));
198 while ((line = reader.readLine()) != null) {
199 if (context.dataSourceIngestIsCancelled()) {
203 String[] values = line.split(
",");
204 TrackPoint point = createTrackPoint(headerMap, values);
206 trackPoints.addPoint(point);
210 }
catch (IOException ex) {
211 throw new DroneIngestException(String.format(
"Failed to read DAT csvFile %s created for AbstractFile: %s", csvFilePath, DATFile.getId()), ex);
224 private Map<String, Integer> makeHeaderMap(String[] headers) {
225 Map<String, Integer> map =
new HashMap<>();
227 for (
int index = 0; index < headers.length; index++) {
228 map.put(headers[index], index);
249 private TrackPoint createTrackPoint(Map<String, Integer> columnLookup, String[] values)
throws DroneIngestException {
251 Double latitude = getDoubleValue(columnLookup.get(HEADER_LAT), values);
252 Double longitude = getDoubleValue(columnLookup.get(HEADER_LONG), values);
254 if (longitude == null || latitude == null) {
259 return new TrackPoint(latitude,
261 getDoubleValue(columnLookup.get(HEADER_ALTITUDE), values),
263 getDoubleValue(columnLookup.get(HEADER_VELOCITY), values),
264 getDoubleValue(columnLookup.get(HEADER_DISTANCE_FROM_HP), values),
265 getDoubleValue(columnLookup.get(HEADER_DISTANCE_TRAVELED), values),
266 getDateTimeValue(columnLookup, values));
279 private Long getDateTimeValue(Map<String, Integer> headerMap, String[] values) {
280 Integer index = headerMap.get(HEADER_DATETILE);
281 if (index == null || index == -1 || index > values.length) {
285 String value = values[index];
286 if (value == null || value.isEmpty()) {
291 ZonedDateTime zdt = ZonedDateTime.parse(value, DateTimeFormatter.ISO_DATE_TIME);
292 return zdt.toLocalDateTime().toEpochSecond(ZoneOffset.UTC);
293 }
catch (DateTimeParseException ex) {
307 private Double getDoubleValue(Integer index, String[] values) {
308 if (index == null || index == -1 || index > values.length) {
312 String value = values[index];
313 if (value == null || value.isEmpty()) {
318 return Double.parseDouble(value);
319 }
catch (NumberFormatException ex) {