Skip to content

Commit

Permalink
Geometric corrections.
Browse files Browse the repository at this point in the history
  • Loading branch information
halmaia committed Jun 27, 2023
1 parent c617aa9 commit eaca99c
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 56 deletions.
1 change: 0 additions & 1 deletion GeoPoint.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Globalization;
using System.Runtime.Serialization;

namespace SL3Reader
{
Expand Down
3 changes: 3 additions & 0 deletions InterferometricMeasurement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,8 @@ public readonly ref struct InterferometricMeasurement
public readonly float Delta;
public readonly float Depth;
public override readonly string ToString() => $"{Delta};{Depth}";

public readonly bool IsValid() =>
Delta is not < 0.001f and not> 5000.0f && Depth is not< 1 and not> 250.0f;
}
}
92 changes: 67 additions & 25 deletions Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,58 @@
using static System.IO.Path;
using static System.Console;
using System.Runtime.CompilerServices;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Collections.ObjectModel;

namespace SL3Reader
{
public static class Program
{
public static void Main(string[] args)
public static void Main([DisallowNull] string[] args)
{
OutputEncoding = System.Text.Encoding.UTF8;
if (args!.Length is not 3) // Always non-null.
{
PrintRed("Wrong number of arguments!");
WriteLine();
PrintUsage();
return;
}

string input = GetFullPath(args![0]);
if (!Exists(input))
if (!Exists(input!))
{
WriteLine("Input file not found!");
PrintRed("Input file not found!");
WriteLine();
PrintUsage();
return;
}

string output = GetFullPath(args![1]);
if (!Exists(GetDirectoryName(output)))

if (IsPathRooted(output!))
{
WriteLine("Directory not found for the output file!");
if (!Exists(output!))
{
PrintRed("Root directory not found!");
WriteLine();
PrintUsage();
return;
}
}

else if (!Exists(GetDirectoryName(output!)))
{
PrintRed("Directory not found for the output file!");
WriteLine();
PrintUsage();
return;
}

using SL3Reader sl3reader = new(input);
using SL3Reader sl3reader = new(input!);

PrintSummary(sl3reader);

string expSelector = args![2].Trim().ToLowerInvariant();
switch (expSelector)
Expand Down Expand Up @@ -66,67 +88,79 @@ public static void Main(string[] args)
break;
case "-u7":
sl3reader.ExportImagery(output, SurveyType.Unknown7);
PrintGreen("Frame type Nr.7 imagery exported successfully.\n");
PrintGreen("Frame type 7 imagery exported successfully.\n");
break;
default:
WriteLine("Invalid third argument (" + expSelector + ").");
WriteLine("Invalid argument (" + expSelector + ").");
PrintUsage();
return;
}

PrintSummary();

return;

static void PrintUsage()
{
WriteLine();

WriteLine("Usage examples:\n");

WriteLine("To export route:");

WriteLine("SL3Reader.exe \"C:\\input.sl3\" \"D:\\output.csv\" -route\n");
WriteLine("\tSL3Reader.exe \"C:\\input.sl3\" \"D:\\output.csv\" -route\n");
WriteLine("To export 3D points with magnetic heading (e.g. measured with devices like Precision–9):");
WriteLine("SL3Reader.exe \"C:\\input.sl3\" \"D:\\output.csv\" -3dm\n");
WriteLine("\tSL3Reader.exe \"C:\\input.sl3\" \"D:\\output.csv\" -3dm\n");
WriteLine("To export 3D points with GNSS heading (e.g. in-built GPS):");
WriteLine("SL3Reader.exe \"C:\\input.sl3\" \"D:\\output.csv\" -3dg\n");
WriteLine("\tSL3Reader.exe \"C:\\input.sl3\" \"D:\\output.csv\" -3dg\n");

WriteLine("To export side scan imagery:");
WriteLine("SL3Reader.exe \"C:\\input.sl3\" \"D:\\OutputFolder\" -ss\n");

WriteLine("\tSL3Reader.exe \"C:\\input.sl3\" \"D:\\OutputFolder\" -ss\n");
WriteLine("To export primary scan imagery:");
WriteLine("SL3Reader.exe \"C:\\input.sl3\" \"D:\\OutputFolder\" -ps\n");
WriteLine("\tSL3Reader.exe \"C:\\input.sl3\" \"D:\\OutputFolder\" -ps\n");
WriteLine("To export secondary scan imagery:");
WriteLine("SL3Reader.exe \"C:\\input.sl3\" \"D:\\OutputFolder\" -ses\n");
WriteLine("\tSL3Reader.exe \"C:\\input.sl3\" \"D:\\OutputFolder\" -ses\n");
WriteLine("To export down scan imagery:");
WriteLine("SL3Reader.exe \"C:\\input.sl3\" \"D:\\OutputFolder\" -ds\n");
WriteLine("\tSL3Reader.exe \"C:\\input.sl3\" \"D:\\OutputFolder\" -ds\n");
WriteLine("To export frame type №7 imagery imagery:");
WriteLine("SL3Reader.exe \"C:\\input.sl3\" \"D:\\OutputFolder\" -u7\n");
WriteLine("\tSL3Reader.exe \"C:\\input.sl3\" \"D:\\OutputFolder\" -u7\n");

WriteLine("If either the input file’s name/path or the output file name/path contains space(s) use double quotation mark (\") to enclose it, like \"D:\\My SL3 Files\\Best Catch.sl3\".\n");

WriteLine("For details see & cite the following publication: Halmai, Ákos; Gradwohl–Valkay, Alexandra; Czigány, Szabolcs; Ficsor, Johanna; Liptay, Zoltán Árpád; Kiss, Kinga; Lóczy, Dénes and Pirkhoffer, Ervin. 2020. \"Applicability of a Recreational-Grade Interferometric Sonar for the Bathymetric Survey and Monitoring of the Drava River\" ISPRS International Journal of Geo-Information 9, no. 3: 149. https://doi.org/10.3390/ijgi9030149 https://www.mdpi.com/2220-9964/9/3/149.\n");
WriteLine("https://github.com/halmaia/SL3Reader");
WriteLine("For license details see: https://github.com/halmaia/SL3Reader/blob/master/LICENSE.\n");
WriteLine();

}

[SkipLocalsInit]
void PrintSummary()
static void PrintSummary(SL3Reader sl3reader)
{
ReadOnlyDictionary<SurveyType, ReadOnlyCollection<nuint>> indexByType = sl3reader.IndexByType;
ReadOnlyCollection<nuint> frames = sl3reader.Frames;
int len = frames.Count;
System.Globalization.CultureInfo invariantCulture = System.Globalization.CultureInfo.InvariantCulture;

WriteLine("File statistics:");
WriteLine("\tNumber of frames: " + sl3reader.Frames.Count.ToString("# ##0"));

System.Collections.ObjectModel.ReadOnlyDictionary<SurveyType, System.Collections.ObjectModel.ReadOnlyCollection<nuint>> indexByType = sl3reader.IndexByType;
WriteLine("\tNumber of frames: " + len.ToString("# ##0"));

WriteLine("\tNumber of primary frames: " + indexByType[SurveyType.Primary].Count.ToString("# ##0"));
WriteLine("\tNumber of secondary frames: " + indexByType[SurveyType.Secondary].Count.ToString("# ##0"));
WriteLine("\tNumber of left sidescan frames: " + indexByType[SurveyType.LeftSidescan].Count.ToString("# ##0"));
WriteLine("\tNumber of right sidescan frames: " + indexByType[SurveyType.RightSidescan].Count.ToString("# ##0"));
WriteLine("\tNumber of sidescan frames: " + indexByType[SurveyType.SideScan].Count.ToString("# ##0"));
WriteLine("\tNumber of downscan frames: " + indexByType[SurveyType.DownScan].Count.ToString("# ##0"));
WriteLine("\tNumber of 3D frames: " + indexByType[SurveyType.ThreeDimensional].Count.ToString("# ##0"));
WriteLine("\tDistance covered: " + sl3reader.AugmentedCoordinates.LastOrDefault().Distance.ToString("0.# m", invariantCulture));

if (len is not 0)
{
unsafe
{
WriteLine("\tBegining of the survey: " + ((Frame*)frames[0])->Timestamp.ToString("yyyy'-'MM'-'dd HH':'mm':'ss.fff'Z'", invariantCulture));
WriteLine("\tEnd of the survey: " + ((Frame*)frames[len - 1])->Timestamp.ToString("yyyy'-'MM'-'dd HH':'mm':'ss.fff'Z'", invariantCulture));
}
}

WriteLine();
PrintGreen("\nExport finished successfully.");
}

[SkipLocalsInit]
Expand All @@ -138,6 +172,14 @@ static void PrintGreen(string message)
ForegroundColor = ConsoleColor.White;
}

[SkipLocalsInit]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static void PrintRed(string message)
{
ForegroundColor = ConsoleColor.Red;
WriteLine(message);
ForegroundColor = ConsoleColor.White;
}
}
}
}
2 changes: 1 addition & 1 deletion Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"profiles": {
"SL3Reader": {
"commandName": "Project",
"commandLineArgs": "\"F:\\Sonar0000.sl3\" \"F:\\SS\\rt.txt\" -route"
"commandLineArgs": "\"F:\\Sonar0003.sl3\" F: -ss"
}
}
}
61 changes: 32 additions & 29 deletions SL3Reader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,16 @@ public class SL3Reader : IDisposable
#endregion End Private variables

[SkipLocalsInit]
public unsafe SL3Reader(string path)
public unsafe SL3Reader([DisallowNull] string path)
{
long len = new FileInfo(path).Length;
ArgumentException.ThrowIfNullOrWhiteSpace(path, nameof(path));
long len = new FileInfo(path!).Length;

if (len < (SLFileHeader.Size + Frame.MinimumInitSize))
throw new EndOfStreamException("The file is too short to be valid.");

memoryMappedFile = MemoryMappedFile.CreateFromFile(
File.OpenHandle(path, FileMode.Open, FileAccess.Read, FileShare.Read, FileOptions.RandomAccess, 0L),
File.OpenHandle(path!, FileMode.Open, FileAccess.Read, FileShare.Read, FileOptions.RandomAccess, 0L),
null, len, MemoryMappedFileAccess.Read, HandleInheritability.None, false);
viewAccessor = memoryMappedFile.CreateViewAccessor(0, len, MemoryMappedFileAccess.Read);
viewHandle = viewAccessor.SafeMemoryMappedViewHandle;
Expand All @@ -56,21 +57,22 @@ public unsafe SL3Reader(string path)
ReadOnlyCollectionBuilder<nuint> frames = new(estimatedCount);

// Init index lists
ReadOnlyCollectionBuilder<nuint> Primary = new(estimatedCount / 8),
Secondary = new(estimatedCount / 8),
DownScan = new(estimatedCount / 10),
int p8 = estimatedCount / 8, p4 = estimatedCount / 4, p10 = estimatedCount / 10;
ReadOnlyCollectionBuilder<nuint> Primary = new(p8),
Secondary = new(p8),
DownScan = new(p10),
LeftSidescan = new(),
RightSidescan = new(),
SideScan = new(estimatedCount / 10),
SideScan = new(p10),
Unknown6 = new(),
Unknown7 = new(estimatedCount / 4),
Unknown8 = new(estimatedCount / 4),
ThreeDimensional = new(estimatedCount / 10),
Unknown7 = new(p4),
Unknown8 = new(p4),
ThreeDimensional = new(p10),
DebugDigital = new(),
DebugNoise = new();

ReadOnlyCollectionBuilder<int> coordinate3DHelper = new(estimatedCount / 10);
ReadOnlyCollectionBuilder<int> coordinateSidescanHelper = new(estimatedCount / 10);
ReadOnlyCollectionBuilder<int> coordinate3DHelper = new(p10);
ReadOnlyCollectionBuilder<int> coordinateSidescanHelper = new(p10);

// Init time
Frame* currentFrame = (Frame*)ptr;
Expand Down Expand Up @@ -253,9 +255,12 @@ public unsafe void ExportImagery(string path, SurveyType surveyType = SurveyType
{
ArgumentNullException.ThrowIfNull(nameof(path));

if (Directory.Exists(path!))
Directory.Delete(path!, true);
path = Directory.CreateDirectory(path!).FullName;
if (!Path.IsPathRooted(path!))
{
if (Directory.Exists(path!))
Directory.Delete(path!, true);
path = Directory.CreateDirectory(path!).FullName;
}

ReadOnlyCollection<nuint> imageFrames = IndexByType[surveyType];
if (imageFrames.Count < 1) return; // Return when no imagery exists.
Expand All @@ -265,6 +270,7 @@ public unsafe void ExportImagery(string path, SurveyType surveyType = SurveyType
List<int> breakpoints = GetBreakPoints(imageFrames, out int maxHeight);
int numberOfColumns = (int)((Frame*)imageFrames[0])->LengthOfEchoData;
byte[] buffer = BitmapHelper.CreateBuffer(maxHeight, numberOfColumns);
string[] worldJoin = new string[6] { "0", "", "", "0", "", "" };

for (int i = 0, maxIndex = breakpoints.Count - 1; i < maxIndex; i++)
{
Expand All @@ -283,7 +289,7 @@ public unsafe void ExportImagery(string path, SurveyType surveyType = SurveyType

string prefix = GetPrefix(surveyType);
using SafeFileHandle handle = File.OpenHandle(Path.Combine(path, prefix + final + ".bmp"),
FileMode.CreateNew, FileAccess.Write, FileShare.None, FileOptions.SequentialScan);
FileMode.Create, FileAccess.Write, FileShare.None, FileOptions.SequentialScan, fileBuffer.Length);
RandomAccess.Write(handle, fileBuffer, 0);
handle.Close();

Expand All @@ -294,18 +300,15 @@ public unsafe void ExportImagery(string path, SurveyType surveyType = SurveyType
var lastStrip = AugmentedCoordinates[Frames.IndexOf(imageFrames[final - 1])];
var lastFrame = (Frame*)imageFrames[final - 1];

double XSize = -(lastStrip.Distance - firstStrip.Distance) / (final - first - 1);
double YSize = -10 * lastFrame->MaxRange * .3048 / numberOfColumns;
string WorldString = string.Join("\r\n",
new string[6]
{"0",
YSize.ToString(InvariantCulture),
XSize.ToString(InvariantCulture),
"0",
lastStrip.Distance.ToString(InvariantCulture),
lastFrame->SurveyType is SurveyType.SideScan ? (-1400*YSize).ToString(): "0"}, 0, 6);

File.WriteAllText(Path.Combine(path, prefix + final + ".bpw"), WorldString);
// TODO: Nem jó!
double XSize = -(lastStrip.Distance - firstStrip.Distance) / (final - first);
double YSize = -10d * lastFrame->MaxRange * .3048d / numberOfColumns;
worldJoin[1] = YSize.ToString(InvariantCulture);
worldJoin[2] = XSize.ToString(InvariantCulture);
worldJoin[4] = lastStrip.Distance.ToString(InvariantCulture);
worldJoin[5] = lastFrame->SurveyType is SurveyType.SideScan ? (-1400d * YSize).ToString(InvariantCulture) : "0";

File.WriteAllText(Path.Combine(path, prefix + final + ".bpw"), string.Join('\n', worldJoin!,0,6));
// End world file
}

Expand Down Expand Up @@ -394,7 +397,7 @@ public unsafe void Export3D(string path, bool includeUnreliable = false, bool ma
var coordinate3DHelper = Coordinate3DHelper;

using StreamWriter streamWriter = File.CreateText(path);
streamWriter.WriteLine("CampaignID,DateTime,X[Lowrance_m],Y[Lowrance_m],Z[m_WGS84Ellipsoid],Depth[m],Angle[°],Distance[m],Reliability");
streamWriter.BaseStream.Write("CampaignID,DateTime,X[Lowrance_m],Y[Lowrance_m],Z[m_WGS84Ellipsoid],Depth[m],Angle[°],Distance[m],Reliability\r\n"u8);

string[] stringArray = GC.AllocateUninitializedArray<string>(9);

Expand Down

0 comments on commit eaca99c

Please sign in to comment.