diff --git a/README.md b/README.md
index a6a294f..df5105d 100644
--- a/README.md
+++ b/README.md
@@ -35,6 +35,17 @@ double metres = depth.AsSiBaseUnit(); // 1.8288
`BritishThermalUnitIT` is the International Table definition (exactly 1055.05585262 J); other BTU definitions differ. US and Imperial liquid measures are named separately. Imperial helpers do not generate SI-prefixed variants.
+Add `DimensionalQuantities.Extensions.Nautical` for international nautical miles, fathoms, square nautical miles and knots. Import its namespace to use the helpers:
+
+```csharp
+using DimensionalQuantities.Extensions.Nautical;
+
+Length passage = Length.FromNauticalMiles(12);
+Speed vesselSpeed = Speed.FromKnots(8);
+```
+
+A nautical mile is exactly 1852 m, and a knot is one nautical mile per hour. Nautical helpers do not generate SI-prefixed variants.
+
```csharp
Voltage voltage = Voltage.FromVolts(12);
Current current = Current.FromAmps(3);
diff --git a/src/DimensionalQuantities.Extensions.Nautical.Tests/DimensionalQuantities.Extensions.Nautical.Tests.csproj b/src/DimensionalQuantities.Extensions.Nautical.Tests/DimensionalQuantities.Extensions.Nautical.Tests.csproj
new file mode 100644
index 0000000..acd5cd6
--- /dev/null
+++ b/src/DimensionalQuantities.Extensions.Nautical.Tests/DimensionalQuantities.Extensions.Nautical.Tests.csproj
@@ -0,0 +1,19 @@
+
+
+ net11.0
+ false
+ true
+ Exe
+ true
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/DimensionalQuantities.Extensions.Nautical.Tests/NauticalConversionsTests.cs b/src/DimensionalQuantities.Extensions.Nautical.Tests/NauticalConversionsTests.cs
new file mode 100644
index 0000000..4067292
--- /dev/null
+++ b/src/DimensionalQuantities.Extensions.Nautical.Tests/NauticalConversionsTests.cs
@@ -0,0 +1,51 @@
+using System.Reflection;
+using DimensionalQuantities.Extensions.Nautical;
+using Xunit;
+
+namespace DimensionalQuantities.Extensions.Nautical.Tests;
+
+public sealed class NauticalConversionsTests
+{
+ [Fact]
+ public void NauticalUnitsConvertToExpectedSiValues()
+ {
+ double[] result =
+ [
+ Length.FromNauticalMiles(1).AsSiBaseUnit(),
+ Length.FromFathoms(1).AsSiBaseUnit(),
+ Area.FromSquareNauticalMiles(1).AsSiBaseUnit(),
+ Speed.FromKnots(1).AsSiBaseUnit(),
+ ];
+
+ double[] expected = [1852d, 1.8288d, 3_429_904d, 1852d / 3600d];
+ Assert.Equal(expected, result);
+ }
+
+ [Fact]
+ public void NauticalUnitsConvertBackFromSiValues()
+ {
+ double[] result =
+ [
+ Length.FromSiBaseUnit(1852).AsNauticalMiles(),
+ Length.FromSiBaseUnit(1.8288).AsFathoms(),
+ Area.FromSiBaseUnit(3_429_904).AsSquareNauticalMiles(),
+ Speed.FromSiBaseUnit(1852d / 3600d).AsKnots(),
+ ];
+
+ Assert.Equal([1d, 1d, 1d, 1d], result);
+ }
+
+ [Fact]
+ public void NauticalUnitsDoNotGenerateSiPrefixedConversions()
+ {
+ string[] names = Assembly.Load("DimensionalQuantities.Extensions.Nautical").GetExportedTypes()
+ .Where(type => type.Namespace == "DimensionalQuantities.Extensions.Nautical")
+ .SelectMany(type => type.GetMethods()).Select(method => method.Name).ToArray();
+
+ Assert.Multiple(
+ () => Assert.DoesNotContain("FromKiloNauticalMiles", names),
+ () => Assert.DoesNotContain("FromMilliKnots", names),
+ () => Assert.DoesNotContain("FromPrefixedUnits", names),
+ () => Assert.DoesNotContain("AsPrefixedUnits", names));
+ }
+}
diff --git a/src/DimensionalQuantities.Extensions.Nautical/DimensionalQuantities.Extensions.Nautical.csproj b/src/DimensionalQuantities.Extensions.Nautical/DimensionalQuantities.Extensions.Nautical.csproj
new file mode 100644
index 0000000..a72f3bb
--- /dev/null
+++ b/src/DimensionalQuantities.Extensions.Nautical/DimensionalQuantities.Extensions.Nautical.csproj
@@ -0,0 +1,35 @@
+
+
+ netstandard2.0
+ GoSim
+ MIT
+ README.md
+ https://github.com/GoSimulate/DimensionalQuantities
+ Named nautical conversions for dimensional quantities.
+ git
+ true
+ $(BaseIntermediateOutputPath)$(Configuration)/$(TargetFramework)/generated
+ true
+ snupkg
+ true
+ true
+ true
+ portable
+
+
+
+
+
+
+
+
+
+
+ <_ProjectReferencesWithVersions Update="@(_ProjectReferencesWithVersions)"
+ Condition="'%(_ProjectReferencesWithVersions.Filename)' == 'DimensionalQuantities'">
+ [$(PackageVersion)]
+
+
+
+
diff --git a/src/DimensionalQuantities.Extensions.Nautical/NauticalSymbols.cs b/src/DimensionalQuantities.Extensions.Nautical/NauticalSymbols.cs
new file mode 100644
index 0000000..f326077
--- /dev/null
+++ b/src/DimensionalQuantities.Extensions.Nautical/NauticalSymbols.cs
@@ -0,0 +1,15 @@
+using DimensionalQuantities.Units.Declarations;
+
+namespace DimensionalQuantities.Extensions.Nautical;
+
+internal static class NauticalSymbols
+{
+ [UnitSymbol("NauticalMile", "NauticalMiles", "nmi", Scale = 1852)]
+ internal sealed class NauticalMile { }
+
+ [UnitSymbol("Fathom", "Fathoms", "ftm", Scale = 1.8288)]
+ internal sealed class Fathom { }
+
+ [UnitSymbol("Knot", "Knots", "kn", Scale = 1852d / 3600d)]
+ internal sealed class Knot { }
+}
diff --git a/src/DimensionalQuantities.Extensions.Nautical/NauticalUnits.cs b/src/DimensionalQuantities.Extensions.Nautical/NauticalUnits.cs
new file mode 100644
index 0000000..50b51c2
--- /dev/null
+++ b/src/DimensionalQuantities.Extensions.Nautical/NauticalUnits.cs
@@ -0,0 +1,23 @@
+using DimensionalQuantities.Units.Declarations;
+
+namespace DimensionalQuantities.Extensions.Nautical;
+
+[UnitProvider]
+internal static class NauticalUnits
+{
+ [QuantityUnit(typeof(Length))]
+ [UnitFactor(typeof(NauticalSymbols.NauticalMile))]
+ private sealed class NauticalMileUnit { }
+
+ [QuantityUnit(typeof(Length))]
+ [UnitFactor(typeof(NauticalSymbols.Fathom))]
+ private sealed class FathomUnit { }
+
+ [QuantityUnit(typeof(Area))]
+ [UnitFactor(typeof(NauticalSymbols.NauticalMile), Power = 2)]
+ private sealed class SquareNauticalMileUnit { }
+
+ [QuantityUnit(typeof(Speed))]
+ [UnitFactor(typeof(NauticalSymbols.Knot))]
+ private sealed class KnotUnit { }
+}
diff --git a/src/DimensionalQuantities.IntegrationTests/Packaging/PackagedGeneratorTests.cs b/src/DimensionalQuantities.IntegrationTests/Packaging/PackagedGeneratorTests.cs
index 2e283fd..77a5551 100644
--- a/src/DimensionalQuantities.IntegrationTests/Packaging/PackagedGeneratorTests.cs
+++ b/src/DimensionalQuantities.IntegrationTests/Packaging/PackagedGeneratorTests.cs
@@ -99,6 +99,31 @@ public static class Consumer
() => Assert.Equal(273.15, InvokeDouble(type, "ConvertTemperature"), 12));
}
+ [Fact]
+ public void NauticalPackageProvidesConversionsThroughCoreDependency()
+ {
+ using PackageRepository repository = PackageRepository.Create(_scratch, new Uri("https://api.nuget.org/v3/index.json"));
+ PackCore(repository);
+ Package nautical = PackSourceProject(repository, "DimensionalQuantities.Extensions.Nautical");
+ ProjectCreator consumer = CreateProject("NauticalConsumer", """
+ using DimensionalQuantities.Extensions.Nautical;
+
+ public static class Consumer
+ {
+ public static double ConvertLength() => Length.FromNauticalMiles(1).AsSiBaseUnit();
+ public static double ConvertSpeed() => Speed.FromKnots(1).AsSiBaseUnit();
+ }
+ """, nautical);
+ Build(consumer);
+
+ using PackageAssemblyContext context = new(OutputDirectory("NauticalConsumer"));
+ Type type = context.LoadFromAssemblyPath(Path.Combine(OutputDirectory("NauticalConsumer"), "NauticalConsumer.dll"))
+ .GetType("Consumer", throwOnError: true)!;
+ Assert.Multiple(
+ () => Assert.Equal(1852, InvokeDouble(type, "ConvertLength")),
+ () => Assert.Equal(1852d / 3600d, InvokeDouble(type, "ConvertSpeed")));
+ }
+
[Fact]
public void MetricNamedConversionsRequireNamespaceImport()
{
diff --git a/src/DimensionalQuantities.IntegrationTests/Packaging/SymbolPackageTests.cs b/src/DimensionalQuantities.IntegrationTests/Packaging/SymbolPackageTests.cs
index f299460..5bfeef4 100644
--- a/src/DimensionalQuantities.IntegrationTests/Packaging/SymbolPackageTests.cs
+++ b/src/DimensionalQuantities.IntegrationTests/Packaging/SymbolPackageTests.cs
@@ -52,6 +52,7 @@ public void CoreSymbolPackageMatchesRuntimeAndAnalyzerAssemblies()
[Theory]
[InlineData("Metric")]
[InlineData("Imperial")]
+ [InlineData("Nautical")]
public void ExtensionSymbolPackageMatchesAssemblyAndEmbedsGeneratedSource(string extension)
{
string projectName = "DimensionalQuantities.Extensions." + extension;
diff --git a/src/DimensionalQuantities.slnx b/src/DimensionalQuantities.slnx
index 4e0c151..75ae68d 100644
--- a/src/DimensionalQuantities.slnx
+++ b/src/DimensionalQuantities.slnx
@@ -11,6 +11,10 @@
+
+
+
+