Compare commits

..

No commits in common. "f8efb09284e3eaf5a5b25505e8956290c012c74d" and "b5e449c1f4644c38f8c0951ef50035df3d33590f" have entirely different histories.

30 changed files with 25 additions and 205 deletions

View file

@ -8,20 +8,15 @@
border-radius: 16px; border-radius: 16px;
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(8px); backdrop-filter: blur(8px);
animation: backdropShift 10s linear infinite;
-webkit-backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
border: 1px solid rgba(18, 47, 101, 0.35); border: 1px solid rgba(18, 47, 101, 0.35);
padding: 2rem; padding: 2rem;
filter: saturate(0.7) brightness(0.8); margin-bottom: 2rem;
position: relative;
filter: saturate(0.9);
z-index: 2; z-index: 2;
} }
.toc:has(#TableOfContents) {
background-color: rgba(25, 25, 35, 0.45);
border-radius: 10px;
max-height: 70vh;
}
.background-container { .background-container {
position: fixed; position: fixed;
top: 0; top: 0;
@ -45,30 +40,15 @@
filter: blur(4px) saturate(0.65); filter: blur(4px) saturate(0.65);
} }
/* Lead Styling */
.prose :where([class~="lead"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
color: rgb(209, 182, 164);
}
/* Header Page Name Styling */
nav > div > a {
font-size: 1.5rem;
text-shadow: 0 0 6px rgba(0, 0, 0, 0.7);
}
/* Header Button Styling */ /* Header Button Styling */
header nav ul li a, header nav ul li a,
header nav ul li button { header nav ul li button {
display: inline-block; display: inline-block;
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
background: rgba(18, 47, 101, 0.25); background-color: rgba(25, 25, 35, 0.25);
border-radius: 8px; border-radius: 8px;
border: 1px solid rgba(25, 25, 35, 0.35); border: 1px solid rgba(25, 25, 35, 0.35);
transition: all 0.3s ease; transition: all 0.3s ease;
backdrop-filter: blur(4px);
animation: backdropShift 10s linear infinite;
-webkit-backdrop-filter: blur(8px);
filter: saturate(0.9) brightness(0.9);
} }
header nav ul li a:hover, header nav ul li a:hover,
@ -81,14 +61,10 @@ header nav ul li button:hover {
/* Search button specific styling */ /* Search button specific styling */
header button[id^="search-button"] { header button[id^="search-button"] {
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
background: rgba(18, 47, 101, 0.25); background-color: rgba(25, 25, 35, 0.25);
border-radius: 8px; border-radius: 8px;
border: 1px solid rgba(25, 25, 35, 0.35); border: 1px solid rgba(25, 25, 35, 0.35);
transition: all 0.3s ease; transition: all 0.3s ease;
backdrop-filter: blur(4px);
animation: backdropShift 10s linear infinite;
-webkit-backdrop-filter: blur(8px);
filter: saturate(0.9) brightness(0.9);
} }
header button[id^="search-button"]:hover { header button[id^="search-button"]:hover {
@ -97,14 +73,18 @@ header button[id^="search-button"]:hover {
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2); box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
} }
@keyframes backdropShift { @keyframes gradientShift {
0% { 0% {
backdrop-filter: blur(8px) hue-rotate(-10deg); filter: saturate(0.9) hue-rotate(-15deg);
} }
50% { 50% {
backdrop-filter: blur(8px) hue-rotate(10deg); filter: saturate(0.9) hue-rotate(15deg);
} }
100% { 100% {
backdrop-filter: blur(8px) hue-rotate(-10deg); filter: saturate(0.9) hue-rotate(-15deg);
} }
} }
article.glass {
animation: gradientShift 30s linear infinite;
}

View file

@ -16,7 +16,7 @@ copyright = "This website is open source. You can find the source code **[here](
name = "Jeremy Karst" name = "Jeremy Karst"
image = "img/sd_eye_icon.webp" image = "img/sd_eye_icon.webp"
headline = "I see patterns where others see noise" headline = "I see patterns where others see noise"
bio = "People think you are smart when you learn for fun." bio = "Seeming smart is easy when you learn for fun."
links = [ links = [
{ email = "mailto:DataScienceDIY@proton.me" }, { email = "mailto:DataScienceDIY@proton.me" },
{ github = "https://code.karsttech.com" }, { github = "https://code.karsttech.com" },

View file

@ -16,4 +16,4 @@ Markdown has been my go-to format for note taking for years now. While searching
This website will be a central place to share my projects, thoughts, and ideas, as well as a landing page for KarstTech. If you would like to see how it is built, I decided to make the source code available at [code.karsttech.com](https://code.karsttech.com). This website will be a central place to share my projects, thoughts, and ideas, as well as a landing page for KarstTech. If you would like to see how it is built, I decided to make the source code available at [code.karsttech.com](https://code.karsttech.com).
![Alt text](karsttech_logo_horiz.png "New KarstTech Logo") ![Alt text](figure.jpg "Image caption")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 KiB

View file

@ -1,9 +1,9 @@
+++ +++
title = 'USM Magnetics - Sensor Fusion' title = 'USM Magnetics'
date = 2025-02-27T07:17:00-05:00 date = 2025-02-27T07:17:00-05:00
draft = false draft = false
categories = ['references'] categories = ['references']
tags = ['USM', 'magnetics', 'data science', 'KarstTech', 'UUV', 'sensor fusion'] tags = ['USM', 'magnetics', 'data science', 'KarstTech', 'UUV']
+++ +++
## Getting Started ## Getting Started
@ -24,15 +24,10 @@ Before we can even attempt to detect targets, we need to convert the vehicle ref
![Earth's Magnetic Field](earth-geomag.webp "Earth's Magnetic Field (courtesy of [Wikipedia](https://en.wikipedia.org/wiki/Earth%27s_magnetic_field))") ![Earth's Magnetic Field](earth-geomag.webp "Earth's Magnetic Field (courtesy of [Wikipedia](https://en.wikipedia.org/wiki/Earth%27s_magnetic_field))")
While the Earth's scale magnetic field is complex, many people assume that on the local scale, the magnetic field is constant. This is not the case, and local variations can be large enough to throw off the vehicle's orientation estimate. In a surface vehicle, this can be mitigated by using GPS to correct for long term drift of the compass / magnetic measurements, but underwater it becomes much more complicated without a detailed map of local magnetic anomalies. Designing a method for planning underwater missions which can correct for these local variations is a problem that is being addressed in the near future, but in short it involves some calibration using surface data, and then using that calibration to improve underwater navigation. While the Earth's scale magnetic field is complex, many people assume that on the local scale, the magnetic field is constant. This is not the case, and local variations can be large enough to throw off the vehicle's orientation estimate.
![Local Magnetic Field Variations](gulfport-geomag.png "Local Magnetic Field Variations near Gulfport, MS") ![Local Magnetic Field Variations](gulfport-geomag.png "Local Magnetic Field Variations near Gulfport, MS")
The sensor fusion process attempts to take the best aspects of each sensor and combine them in a way that provides the most accurate estimate of the vehicle's orientation and position. For Gyroscopes, which are very accurate at measuring short term rotations, but suffer from drift over time, we can use the magnetometer to correct for the drift. We can use accelerometers to correct for quick movements, and to measure the long term orientation relative to gravity, while using GPS to provide long term movement and position data. Magnetometers are immune to the errors introduced into accelerometers by quick movements, especially in surface waves, and so they provide a very accurate measurement of the vehicle's orientation, but are subject to small errors due to local magnetic anomalies, and due to magnetic interference from the vehicle itself.
The exact method used to fuse the data is highly platform specific and needs to be tuned for the physical characteristics of the vehicle, and its sensors. The diagram below shows the general process, but the exact implementation details will not be discussed here.
{{< mermaid >}} {{< mermaid >}}
flowchart TD flowchart TD
subgraph "Input Sensors" subgraph "Input Sensors"
@ -99,5 +94,8 @@ flowchart TD
MAG_CORR --> TARGET MAG_CORR --> TARGET
{{< /mermaid >}} {{< /mermaid >}}
Once the vehicle know's it's own position and orientation through sensor fusion, we can use that information to convert the location of detected objects near the vehicle into the Earth's reference frame. This allows combining measurements from multiple passes over the same area, by multiple vehicles, or even on different days.

View file

@ -1,36 +0,0 @@
{{ define "main" }}
<article class="prose max-w-full dark:prose-invert glass">
{{ with .Title }}
<header>
<h1>{{ . | emojify }}</h1>
</header>
{{ end }}
<section>{{ .Content | emojify }}</section>
<section>
{{ if .Data.Pages }}
{{ if $.Params.groupByYear | default ($.Site.Params.list.groupByYear | default true) }}
{{ range (.Paginate (.Pages.GroupByDate "2006")).PageGroups }}
<h2 class="mt-12 text-2xl font-bold text-neutral-700 first:mt-8 dark:text-neutral-300">
{{ .Key }}
</h2>
<hr class="w-36 border-dotted border-neutral-400" />
{{ range .Pages }}
{{ partial "article-link.html" . }}
{{ end }}
{{ end }}
{{ else }}
{{ range (.Paginate .Pages).Pages }}
{{ partial "article-link.html" . }}
{{ end }}
{{ end }}
{{ partial "pagination.html" . }}
{{ else }}
<section class="prose mt-10 dark:prose-invert">
<p class="border-t py-8">
<em>{{ i18n "list.no_articles" | emojify }}</em>
</p>
</section>
{{ end }}
</section>
</article>
{{ end }}

View file

@ -1,15 +0,0 @@
{{ $images := slice }}
{{ range readDir "static/backgrounds" }}
{{ if in .Name ".webp" }}
{{ $images = $images | append .Name }}
{{ end }}
{{ end }}
<div class="background-container">
<img id="background-image" alt="Background" class="background-image">
<script>
const images = {{ $images }};
const randomImage = images[Math.floor(Math.random() * images.length)];
document.getElementById('background-image').src = `/backgrounds/${randomImage}`;
</script>
</div>

View file

@ -1,2 +1,2 @@
<link rel="shortcut icon" href="/android-chrome-192x192.png"> <link rel="shortcut icon" href="web-app-manifest-192x192.png">
<link rel="apple-touch-icon" sizes="192x192" href="/android-chrome-192x192.png"> <link rel="apple-touch-icon" sizes="192x192" href="/web-app-manifest-192x192.png">

View file

@ -1,5 +1,5 @@
<div class="background-container"> <div class="background-container">
<img src="/Apophysis-071008-64.webp" alt="Background" class="background-image"> <img src="/neuron-purple.webp" alt="Background" class="background-image">
</div> </div>
<article class="prose max-w-full dark:prose-invert glass"> <article class="prose max-w-full dark:prose-invert glass">
{{ with .Title }} {{ with .Title }}

View file

@ -1,3 +0,0 @@
{{ if .Params.showTableOfContents | default (.Site.Params.article.showTableOfContents | default false) }}
<script src="/js/scrollspy.js"></script>
{{ end }}

View file

@ -1,37 +0,0 @@
{{ define "main" }}
{{ partial "background-images.html" . }}
<article class="prose max-w-full dark:prose-invert glass">
{{ with .Title }}
<header>
<h1>{{ . | emojify }}</h1>
</header>
{{ end }}
<section>{{ .Content | emojify }}</section>
<section>
{{ if .Data.Pages }}
{{ if $.Params.groupByYear | default ($.Site.Params.list.groupByYear | default true) }}
{{ range (.Paginate (.Pages.GroupByDate "2006")).PageGroups }}
<h2 class="mt-12 text-2xl font-bold text-neutral-700 first:mt-8 dark:text-neutral-300">
{{ .Key }}
</h2>
<hr class="w-36 border-dotted border-neutral-400" />
{{ range .Pages }}
{{ partial "article-link.html" . }}
{{ end }}
{{ end }}
{{ else }}
{{ range (.Paginate .Pages).Pages }}
{{ partial "article-link.html" . }}
{{ end }}
{{ end }}
{{ partial "pagination.html" . }}
{{ else }}
<section class="prose mt-10 dark:prose-invert">
<p class="border-t py-8">
<em>{{ i18n "list.no_articles" | emojify }}</em>
</p>
</section>
{{ end }}
</section>
</article>
{{ end }}

View file

@ -1,67 +0,0 @@
{{ define "main" }}
{{ partial "background-images.html" . }}
{{- $images := .Resources.ByType "image" }}
{{- $cover := $images.GetMatch (.Params.cover | default "*cover*") }}
{{- $feature := $images.GetMatch (.Params.feature | default "*feature*") | default $cover }}
<article class="prose max-w-full dark:prose-invert glass">
<header class="max-w-prose">
{{ if .Params.showBreadcrumbs | default (.Site.Params.article.showBreadcrumbs | default false) }}
{{ partial "breadcrumbs.html" . }}
{{ end }}
<h1 class="mb-8 mt-0 text-4xl font-extrabold text-neutral-900 dark:text-neutral">
{{ .Title | emojify }}
</h1>
{{ if or
(.Params.showDate | default (.Site.Params.article.showDate | default true))
(and (.Params.showDateUpdated | default (.Site.Params.article.showDateUpdated | default false)) (ne (partial "functions/date.html" .Date) (partial "functions/date.html" .Lastmod)))
(and (.Params.showWordCount | default (.Site.Params.article.showWordCount | default false)) (ne .WordCount 0))
(and (.Params.showReadingTime | default (.Site.Params.article.showReadingTime | default true)) (ne .ReadingTime 0))
(.Params.showEdit | default (.Site.Params.article.showEdit | default false))
}}
<div class="mb-10 text-base text-neutral-500 dark:text-neutral-400 print:hidden">
{{ partial "article-meta.html" (dict "context" . "scope" "single") }}
</div>
{{ end }}
{{ with $feature }}
<div class="prose">
{{ $altText := $.Params.featureAlt | default $.Params.coverAlt | default "" }}
{{ $class := "mb-6 rounded-md" }}
{{ $webp := $.Page.Site.Params.enableImageWebp | default true }}
{{ partial "picture.html" (dict "img" . "alt" $altText "class" $class "lazy" false "webp" $webp) }}
{{ with $.Params.coverCaption }}
<figcaption class="-mt-3 mb-6 text-center">{{ . | markdownify }}</figcaption>
{{ end }}
</div>
{{ end }}
</header>
<section class="prose mt-0 flex max-w-full flex-col dark:prose-invert lg:flex-row">
{{ if and (.Params.showTableOfContents | default (.Site.Params.article.showTableOfContents | default false)) (in .TableOfContents "<ul") }}
<div class="order-first px-0 lg:order-last lg:max-w-xs lg:ps-8">
<div class="toc pe-5 lg:sticky lg:top-10 print:hidden">
{{ partial "toc.html" . }}
</div>
</div>
{{ end }}
<div class="min-h-0 min-w-0 max-w-prose grow">
{{ .Content | emojify }}
</div>
</section>
<footer class="max-w-prose pt-8 print:hidden">
{{ partial "author.html" . }}
{{ partial "sharing-links.html" . }}
{{ partial "article-pagination.html" . }}
{{ if .Params.showComments | default (.Site.Params.article.showComments | default false) }}
{{ if templates.Exists "partials/comments.html" }}
<div class="pt-3">
<hr class="border-dotted border-neutral-300 dark:border-neutral-600" />
<div class="pt-3">
{{ partial "comments.html" . }}
</div>
</div>
{{ else }}
{{ warnf "[CONGO] Comments are enabled for %s but no comments partial exists." .File.Path }}
{{ end }}
{{ end }}
</footer>
</article>
{{ end }}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 390 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 287 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 334 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 360 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 249 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 496 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 227 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

View file

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 124 KiB