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) {