Finished ross content, added skill matrix, li, and ul, and improved many existing components, created and refactored to unified layouts and grid, visual improvements with proper column to row collapsing #13

Merged
caperren merged 2 commits from website-content-updates into main 2025-12-06 18:07:53 +00:00
29 changed files with 770 additions and 358 deletions
Showing only changes of commit 87224a6dbb - Show all commits

View File

@@ -1,6 +1,9 @@
ADCP ADCP
Altium
ASSEM ASSEM
astrojs astrojs
Atmel
barebones
Candian Candian
caperren caperren
CEOAS CEOAS
@@ -10,6 +13,7 @@ CONSERV
Corwin Corwin
dangerousthings dangerousthings
Dechorionator Dechorionator
ebox
fhhs fhhs
flowbite flowbite
HDFS HDFS
@@ -19,10 +23,13 @@ hwupload
iceops iceops
ITAR ITAR
Jetson Jetson
KFSK
leconte leconte
Loctite Loctite
luxon luxon
MGMT MGMT
Mokai
Multimeters
nixos nixos
offroad offroad
Onshape Onshape
@@ -30,6 +37,7 @@ OSSM
OSURC OSURC
Perren Perren
Perren's Perren's
Pixhawk
pubpath pubpath
RFID RFID
RSSI RSSI

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@@ -2,4 +2,4 @@
--- ---
<h1 class="text-xl font-extrabold md:text-3xl"><slot /></h1> <h1 class="text-xl font-extrabold sm:text-2xl md:text-3xl"><slot /></h1>

View File

@@ -2,4 +2,4 @@
--- ---
<h2 class="my-4 font-bold md:text-2xl"><slot /></h2> <h2 class="text-lg font-bold sm:text-xl md:text-2xl"><slot /></h2>

View File

@@ -2,4 +2,4 @@
--- ---
<h3 class="mt-4 mb-2 font-bold md:text-lg"><slot /></h3> <h3 class="text-md font-semibold sm:text-lg md:text-xl"><slot /></h3>

9
src/components/Li.astro Normal file
View File

@@ -0,0 +1,9 @@
---
---
<>
<li>
<slot />
</li>
</>

View File

@@ -9,10 +9,12 @@ interface Props {
const { title, href, target = "_blank" } = Astro.props; const { title, href, target = "_blank" } = Astro.props;
--- ---
<a <div class="mx-auto">
class="text-caperren-green border-caperren-green hover:border-caperren-green-light hover:text-caperren-green-light rounded-2xl border-2 bg-black p-2" <a
href={href} class="text-caperren-green border-caperren-green hover:border-caperren-green-light hover:text-caperren-green-light rounded-2xl border-2 bg-black p-2"
target={target} href={href}
> target={target}
{title} >
</a> {title}
</a>
</div>

View File

@@ -39,7 +39,7 @@ import { Image } from "astro:assets";
</svg> </svg>
</button> </button>
<div <div
class="z-40 mt-1 hidden w-full lg:block lg:w-auto" class="z-40 mt-6 hidden w-full lg:mt-0 lg:block lg:w-auto"
id="navbar-multi-level" id="navbar-multi-level"
> >
<NestedNavbarEntry items={siteLayout} /> <NestedNavbarEntry items={siteLayout} />

View File

@@ -19,29 +19,34 @@ const { pathname } = Astro.url;
--- ---
<ul <ul
class={"flex flex-col p-4 bg-black border-caperren-green " + class:list={[
(depth ? "space-y-2" : "items-start lg:flex-row lg:space-x-8 lg:mt-0 ")} "border-caperren-green flex flex-col space-y-4 space-x-8 bg-black",
depth
? "space-y-4 py-4"
: "items-start lg:mt-0 lg:flex-row lg:space-y-0 lg:space-x-8",
]}
> >
{ {
items.map( items.map(
(entry) => (entry) =>
(entry.enabled ?? true) && ( (entry.enabled ?? true) && (
<li> <li class="">
{Array.isArray(entry.children) && entry.children.length ? ( {Array.isArray(entry.children) && entry.children.length ? (
<div <div>
class={
pathname.startsWith(getHrefPath(entry))
? "border-caperren-green border-b-2"
: ""
}
>
<button <button
id={"dropdownNavbarLink" + getNavLinkSuffix(entry)} id={"dropdownNavbarLink" + getNavLinkSuffix(entry)}
data-dropdown-toggle={ data-dropdown-toggle={
"dropdownNavbar" + getNavLinkSuffix(entry) "dropdownNavbar" + getNavLinkSuffix(entry)
} }
data-dropdown-placement="bottom" data-dropdown-placement="bottom-start"
class="hover:text-caperren-green-light lg:hover:text-caperren-green-light flex w-full items-center justify-between px-3 py-2 lg:p-0 lg:hover:bg-transparent" data-dropdown-offset-distance="5"
data-dropdown-offset-skidding="12"
class:list={[
"hover:text-caperren-green-light lg:hover:text-caperren-green-light flex w-full items-center justify-between lg:p-0 lg:hover:bg-transparent",
pathname.startsWith(getHrefPath(entry))
? "border-caperren-green border-b-2"
: false,
]}
> >
{entry.navText} {entry.navText}
<svg <svg
@@ -61,7 +66,7 @@ const { pathname } = Astro.url;
</button> </button>
<div <div
id={"dropdownNavbar" + getNavLinkSuffix(entry)} id={"dropdownNavbar" + getNavLinkSuffix(entry)}
class="border-caperren-green z-10 hidden w-screen border bg-black shadow-sm lg:w-max" class="border-caperren-green z-10 hidden w-max max-w-screen border bg-black px-6 shadow-sm"
> >
<Astro.self <Astro.self
items={entry.children} items={entry.children}
@@ -71,17 +76,16 @@ const { pathname } = Astro.url;
</div> </div>
</div> </div>
) : ( ) : (
<div <div>
class={
pathname === getHrefPath(entry)
? "border-caperren-green border-b-2"
: ""
}
>
<a <a
href={getHrefPath(entry)} href={getHrefPath(entry)}
target={getHrefPath(entry).startsWith("/") ? "" : "_blank"} target={getHrefPath(entry).startsWith("/") ? "" : "_blank"}
class="hover:text-caperren-green-light ring-caperren-green-dark block bg-transparent px-3 py-2 lg:p-0" class:list={[
"hover:text-caperren-green-light ring-caperren-green-dark block bg-transparent lg:p-0",
pathname === getHrefPath(entry)
? "border-caperren-green border-b-2"
: false,
]}
aria-current={ aria-current={
pathname === getHrefPath(entry) ? "page" : undefined pathname === getHrefPath(entry) ? "page" : undefined
} }

38
src/components/Ol.astro Normal file
View File

@@ -0,0 +1,38 @@
---
import Li from "@components/Li.astro";
import type { ComponentPropsBase } from "@interfaces/components.ts";
import type { lineItem } from "@interfaces/ul-li.ts";
interface Props extends ComponentPropsBase {
lineItems?: lineItem[];
depth?: number;
}
const { class: className, lineItems, depth = 0 } = Astro.props;
---
<ol
class:list={["list-inside list-disc", className, depth > 0 ? "ps-3" : false]}
>
{
lineItems ? (
lineItems.map((line) => (
<Li>
{line.item}
{line.subItems ? (
<Astro.self
class={className}
lineItems={line.subItems}
depth={depth + 1}
/>
) : undefined}
</Li>
))
) : (
<Li>
<slot />
</Li>
)
}
</ol>

View File

@@ -0,0 +1,21 @@
---
const hasHeader = Astro.slots.has("header");
const hasDefault = Astro.slots.has("default");
---
<div class="grid grid-cols-1 gap-3">
{
Astro.slots.has("header") && (
<div>
<slot name="header" />
</div>
)
}
{
Astro.slots.has("default") && (
<div class="grid grid-cols-1 gap-3">
<slot />
</div>
)
}
</div>

View File

@@ -1,7 +1,13 @@
--- ---
import type { ComponentPropsBase } from "@interfaces/components.ts";
interface Props extends ComponentPropsBase {
initialTab?: boolean;
}
const { class: className, initialTab = true } = Astro.props;
---
--- <div class:list={className}>
{initialTab && <>&ensp;</>}<slot />
<div class="">
<slot />
</div> </div>

View File

@@ -2,6 +2,6 @@
--- ---
<div class="space-y-2"> <div class="grid grid-cols-1 gap-2">
<slot /> <slot />
</div> </div>

View File

@@ -7,6 +7,10 @@ const keys: { [key: string]: string } = {
ADCP: "Acoustic doppler current profiler", ADCP: "Acoustic doppler current profiler",
COTS: "Consumer off-the-shelf", COTS: "Consumer off-the-shelf",
CTD: "Conductivity, temperature, and depth sensor", CTD: "Conductivity, temperature, and depth sensor",
GUI: "Graphical user interface",
NUC: "A small and low-power computer made by Intel",
PCBs: "Printed circuit boards",
UPS: "Uninterruptible power supply",
}; };
const key: string | undefined = Astro.props.key; const key: string | undefined = Astro.props.key;

View File

@@ -0,0 +1,30 @@
---
import H3 from "@components/H3.astro";
import PageGroup from "@components/PageGroup.astro";
import Ul from "@components/Ul.astro";
import type { categorySkills } from "@interfaces/skill-matrix.ts";
interface Props {
categorizedSkills: categorySkills[];
}
const { categorizedSkills } = Astro.props;
---
<PageGroup>
<Fragment slot="header"><H3>Relevant Skills</H3></Fragment>
<div
class="border-caperren-green grid grid-flow-row gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"
>
{
categorizedSkills.map((categorySkills) => (
<div>
<div class="text-sm font-extrabold">{categorySkills.category}</div>
<hr class="text-caperren-green" />
<Ul class="text-sm" lineItems={categorySkills.skills} />
</div>
))
}
</div>
</PageGroup>

38
src/components/Ul.astro Normal file
View File

@@ -0,0 +1,38 @@
---
import Li from "@components/Li.astro";
import type { ComponentPropsBase } from "@interfaces/components.ts";
import type { lineItem } from "@interfaces/ul-li.ts";
interface Props extends ComponentPropsBase {
lineItems?: lineItem[];
depth?: number;
}
const { class: className, lineItems, depth = 0 } = Astro.props;
---
<ul
class:list={["list-inside list-disc", className, depth > 0 ? "ps-3" : false]}
>
{
lineItems ? (
lineItems.map((line) => (
<Li>
{line.item}
{line.subItems ? (
<Astro.self
class={className}
lineItems={line.subItems}
depth={depth + 1}
/>
) : undefined}
</Li>
))
) : (
<Li>
<slot />
</Li>
)
}
</ul>

View File

@@ -4,7 +4,7 @@ export const siteLayout: navLink[] = [
{ navText: "About", path: "" }, { navText: "About", path: "" },
{ navText: "Education", path: "education" }, { navText: "Education", path: "education" },
{ {
navText: "Experiences", navText: "Experience",
path: "experience", path: "experience",
children: [ children: [
{ {
@@ -29,7 +29,6 @@ export const siteLayout: navLink[] = [
path: "osu-ceoas-ocean-mixing-group", path: "osu-ceoas-ocean-mixing-group",
children: [ children: [
{ {
enabled: false,
navText: "Robotics Oceanographic Surface Sampler", navText: "Robotics Oceanographic Surface Sampler",
path: "robotic-oceanographic-surface-sampler", path: "robotic-oceanographic-surface-sampler",
}, },

View File

@@ -0,0 +1,6 @@
import type { lineItem } from "@interfaces/ul-li.ts";
export interface categorySkills {
category: string;
skills: lineItem[];
}

4
src/interfaces/ul-li.ts Normal file
View File

@@ -0,0 +1,4 @@
export interface lineItem {
item: string;
subItems?: lineItem[];
}

View File

@@ -4,6 +4,7 @@ import "@styles/global.css";
import Footer from "@components/Footer.astro"; import Footer from "@components/Footer.astro";
import H1 from "@components/H1.astro"; import H1 from "@components/H1.astro";
import Navbar from "@components/Navbar.astro"; import Navbar from "@components/Navbar.astro";
import PageGroup from "@components/PageGroup.astro";
import { pathToMetadata } from "@data/site-layout.ts"; import { pathToMetadata } from "@data/site-layout.ts";
@@ -39,28 +40,32 @@ const pageEnabled = pathToMetadata(Astro.url.pathname).enabled ?? true;
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{pageEnabled ? pageTitle : "Corwin Perren"}</title> <title>{pageEnabled ? pageTitle : "Corwin Perren"}</title>
</head> </head>
<body class="flex h-dvh w-full max-w-full flex-col bg-black text-white"> <body
class="grid h-dvh w-full max-w-full grid-cols-1 gap-0 bg-black text-white"
>
<div <div
id="content-body-scrolling" id="content-body-scrolling"
class="grow overflow-x-hidden overflow-y-scroll" class="grow overflow-x-hidden overflow-y-scroll"
> >
<Navbar /> <Navbar />
<main class="mx-6 my-6"> <main class="mx-6 my-2">
<div class="mb-2 md:mb-6"> {
{ showTitle && pageEnabled && (
title && showTitle && pageEnabled && ( <PageGroup>
<H1 bottomMargin={!subTitles}>{title}</H1> <Fragment slot="header">
) <div class="leading-3">
} <H1>{title}</H1>
{ {subTitles?.map((subTitle) => (
showTitle && <p class="md:text-md text-sm font-bold italic">
pageEnabled && {subTitle}
subTitles?.map((subTitle) => ( </p>
<p class="text-sm font-bold md:text-xl">{subTitle}</p> ))}
)) </div>
} </Fragment>
</div> </PageGroup>
<div> )
}
<div class="grid grid-cols-1 gap-4">
{pageEnabled ? <slot /> : <H1>Under Construction</H1>} {pageEnabled ? <slot /> : <H1>Under Construction</H1>}
</div> </div>
</main> </main>

View File

@@ -1,10 +1,11 @@
--- ---
import PdfViewer from "@components/Media/PdfViewer.astro";
import BaseLayout from "@layouts/BaseLayout.astro"; import BaseLayout from "@layouts/BaseLayout.astro";
import PdfViewer from "@components/Media/PdfViewer.astro";
--- ---
<BaseLayout {...Astro.props}> <BaseLayout {...Astro.props}>
<div class="h-dvh"> <div class="mt-2 h-dvh">
<PdfViewer class="mx-auto" pdf={Astro.props.resume} /> <PdfViewer pdf={Astro.props.resume} />
</div> </div>
</BaseLayout> </BaseLayout>

View File

@@ -5,6 +5,7 @@ import H2 from "@components/H2.astro";
import H3 from "@components/H3.astro"; import H3 from "@components/H3.astro";
import InlineLink from "@components/InlineLink.astro"; import InlineLink from "@components/InlineLink.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro"; import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import PageGroup from "@components/PageGroup.astro";
import Table from "@components/Table.astro"; import Table from "@components/Table.astro";
import Timeline from "@components/Timeline/Timeline.astro"; import Timeline from "@components/Timeline/Timeline.astro";
@@ -108,16 +109,24 @@ const courseTable: tableData = {
<BaseLayout title="Education"> <BaseLayout title="Education">
<Carousel carouselGroup={diplomaCarouselGroup} /> <Carousel carouselGroup={diplomaCarouselGroup} />
<H2>Timeline</H2> <PageGroup>
<Timeline timeline={timeline} /> <Fragment slot="header"><H2>Timeline</H2></Fragment>
<H2>Oregon State University</H2> <Timeline timeline={timeline} />
<H3> </PageGroup>
<InlineLink <PageGroup>
class="font-bold md:text-lg" <Fragment slot="header"><H2>Oregon State University</H2></Fragment>
href="https://github.com/caperren/school_archives/tree/master/OSU%20Coursework" <PageGroup>
>Coursework Archives</InlineLink <Fragment slot="header">
> <InlineLink
</H3> class="font-bold md:text-lg"
<H3>Course Listing</H3> href="https://github.com/caperren/school_archives/tree/master/OSU%20Coursework"
<Table data={courseTable} /> ><H3>Coursework Archives</H3></InlineLink
>
</Fragment>
</PageGroup>
<PageGroup>
<Fragment slot="header"><H3>Course Listing</H3></Fragment>
<Table data={courseTable} />
</PageGroup>
</PageGroup>
</BaseLayout> </BaseLayout>

View File

@@ -3,8 +3,13 @@ import ExperienceLayout from "@layouts/ExperienceLayout.astro";
import H2 from "@components/H2.astro"; import H2 from "@components/H2.astro";
import H3 from "@components/H3.astro"; import H3 from "@components/H3.astro";
import InlineLink from "@components/InlineLink.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro"; import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import Video from "@components/Media/Video.astro";
import PageGroup from "@components/PageGroup.astro";
import Paragraph from "@components/Paragraph.astro"; import Paragraph from "@components/Paragraph.astro";
import Paragraphs from "@components/Paragraphs.astro";
import PopoverWordDefinition from "@components/PopoverWordDefinition.astro";
import Timeline from "@components/Timeline/Timeline.astro"; import Timeline from "@components/Timeline/Timeline.astro";
import type { carouselGroup } from "@interfaces/image-carousel.ts"; import type { carouselGroup } from "@interfaces/image-carousel.ts";
@@ -56,83 +61,89 @@ const headerCarouselGroup: carouselGroup = {
], ],
}; };
import InlineLink from "@components/InlineLink.astro";
import Video from "@components/Media/Video.astro";
import Paragraphs from "@components/Paragraphs.astro";
import PopoverWordDefinition from "@components/PopoverWordDefinition.astro";
import { subTitles } from "./osu-ceoas-ocean-mixing-group.ts"; import { subTitles } from "./osu-ceoas-ocean-mixing-group.ts";
--- ---
<ExperienceLayout title="LeConte Glacier Deployments" subTitles={subTitles}> <ExperienceLayout title="LeConte Glacier Deployments" subTitles={subTitles}>
<Carousel carouselGroup={headerCarouselGroup} /> <Carousel carouselGroup={headerCarouselGroup} />
<H2>Summary</H2> <PageGroup>
<H3>Timeline</H3> <Fragment slot="header"><H2>Summary</H2></Fragment>
<Timeline timeline={deploymentTimeline} /> <PageGroup>
<H3>Location</H3> <Fragment slot="header"><H3>Timeline</H3></Fragment>
<iframe <Timeline timeline={deploymentTimeline} />
class="w-full" </PageGroup>
width="600" <PageGroup>
height="450" <Fragment slot="header"><H3>Location</H3></Fragment>
src="https://maps.google.com/maps?q=leconte%20glacier&t=k&z=11&ie=UTF8&iwloc=B&output=embed" <iframe
></iframe> class="w-full"
<H2>Details</H2> width="600"
<Paragraphs> height="450"
<Paragraph> src="https://maps.google.com/maps?q=leconte%20glacier&t=k&z=11&ie=UTF8&iwloc=B&output=embed"
As part of my time working on the ></iframe>
<InlineLink </PageGroup>
href="/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler" </PageGroup>
> <PageGroup>
Robotic Oceanographic Surface Sampler</InlineLink <Fragment slot="header"><H2>Details</H2></Fragment>
>, I had the fantastic opportunity to be deployed at the LeConte Glacier <Paragraphs>
in Alaska! This started in early 2017 with setup and ocean trials in <Paragraph>
nearby Petersburg. The team had sent multiple shipping containers with our As part of my time working on the
robotic platforms and most equipment to assemble, test, and debug them a <InlineLink
few months prior, allowing us to get to work the moment we arrived. We href="/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler"
spent multiple weeks at the docks with our makeshift workstations built >
from plywood and pelican cases, validating the hardware we'd sent, and Robotic Oceanographic Surface Sampler</InlineLink
making adjustments with improved hardware we'd hand-carried on our >, I had the fantastic opportunity to be deployed at the LeConte Glacier
flights. This also provided a great opportunity to work out any final in Alaska! This started in early 2017 with setup and ocean trials in
firmware and/or software bugs while the vehicles were still relatively nearby Petersburg. The team had sent multiple shipping containers with
easy to retrieve. After a short trip back home to recover, and prep any our robotic platforms and most equipment to assemble, test, and debug
last minute items we'd forgotten, our research team flew back and headed them a few months prior, allowing us to get to work the moment we
for the glacier! arrived. We spent multiple weeks at the docks with our makeshift
</Paragraph> workstations built from plywood and pelican cases, validating the
<Paragraph> hardware we'd sent, and making adjustments with improved hardware we'd
The towering mountain of ice sits roughly 30 miles from Petersburg, so hand-carried on our flights. This also provided a great opportunity to
we'd commissioned an off-season fishing vessel, Steller, and it's crew, to work out any final firmware and/or software bugs while the vehicles were
take us as close to it as was reasonably safe. The team worked 24 hours a still relatively easy to retrieve. After a short trip back home to
day, on two shifts, deploying and retrieving the ROSS platforms, recover, and prep any last minute items we'd forgotten, our research
performing repairs (as needed), recovering/processing collected data, team flew back and headed for the glacier!
manually deploying the ship's <PopoverWordDefinition key="CTD" />, and </Paragraph>
occasionally spending considerable time pushing icebergs the size of <Paragraph>
houses away from an <PopoverWordDefinition key="ADCP" /> mounted to Steller The towering mountain of ice sits roughly 30 miles from Petersburg, so
using fiberglass poles. Many hardware failures had to be solved during these we'd commissioned an off-season fishing vessel, Steller, and it's crew,
long days, and it was a very rewarding and creative experience to work around to take us as close to it as was reasonably safe. The team worked 24
the limitations of this isolated (and salty) environment. hours a day, on two shifts, deploying and retrieving the ROSS platforms,
</Paragraph> performing repairs (as needed), recovering/processing collected data,
<Paragraph> manually deploying the ship's <PopoverWordDefinition key="CTD" />, and
On top of being a unique engineering and team building experience, LeConte occasionally spending considerable time pushing icebergs the size of
lives among the most beautiful places I've yet to experience in this life. houses away from an <PopoverWordDefinition key="ADCP" /> mounted to Steller
There's something special about being somewhere so incredibly remote and using fiberglass poles. Many hardware failures had to be solved during these
untouched by humans. The pristine evergreen forests, eerie blue-green hues long days, and it was a very rewarding and creative experience to work around
of the glacier and icebergs, ancient towering mountains, and genuinely the limitations of this isolated (and salty) environment.
curious looks from local land and marine life unfamiliar with human </Paragraph>
presence made it humbly clear that for once we as humans were the odd ones <Paragraph>
out. These trips were ones that I will treasure and think back on fondly On top of being a unique engineering and team building experience,
on for the rest of my life. LeConte lives among the most beautiful places I've yet to experience in
</Paragraph> this life. There's something special about being somewhere so incredibly
</Paragraphs> remote and untouched by humans. The pristine evergreen forests, eerie
<H2>Videos</H2> blue-green hues of the glacier and icebergs, ancient towering mountains,
<div class="grid grid-cols-1 gap-4 md:grid-cols-2"> and genuinely curious looks from local land and marine life unfamiliar
{ with human presence made it humbly clear that for once we as humans were
videos.map((video) => ( the odd ones out. These trips were ones that I will treasure and think
<Video back on fondly on for the rest of my life.
videoPath={video} </Paragraph>
autoPlay={true} </Paragraphs>
loop={true} </PageGroup>
playsInline={true} <PageGroup>
/> <Fragment slot="header"><H2>Videos</H2></Fragment>
)) <div class="grid grid-cols-1 gap-4 md:grid-cols-2">
} {
</div> videos.map((video) => (
<Video
videoPath={video}
autoPlay={true}
loop={true}
playsInline={true}
/>
))
}
</div>
</PageGroup>
</ExperienceLayout> </ExperienceLayout>

View File

@@ -26,4 +26,5 @@ export const deploymentTimeline: timelineEntry[] = [
export const subTitles = [ export const subTitles = [
"Oregon State University", "Oregon State University",
"College of Earth, Ocean, and Atmospheric Sciences", "College of Earth, Ocean, and Atmospheric Sciences",
"Ocean Mixing Group",
]; ];

View File

@@ -3,17 +3,26 @@ import ExperienceLayout from "@layouts/ExperienceLayout.astro";
import H2 from "@components/H2.astro"; import H2 from "@components/H2.astro";
import H3 from "@components/H3.astro"; import H3 from "@components/H3.astro";
import InlineLink from "@components/InlineLink.astro";
import Li from "@components/Li.astro";
import LinkButton from "@components/LinkButton.astro"; import LinkButton from "@components/LinkButton.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro"; import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import PdfViewer from "@components/Media/PdfViewer.astro"; import PdfViewer from "@components/Media/PdfViewer.astro";
import PageGroup from "@components/PageGroup.astro";
import Paragraph from "@components/Paragraph.astro";
import Paragraphs from "@components/Paragraphs.astro";
import PopoverWordDefinition from "@components/PopoverWordDefinition.astro"; import PopoverWordDefinition from "@components/PopoverWordDefinition.astro";
import SkillMatrix from "@components/SkillMatrix/SkillMatrix.astro";
import Timeline from "@components/Timeline/Timeline.astro"; import Timeline from "@components/Timeline/Timeline.astro";
import Ul from "@components/Ul.astro";
import type { carouselGroup } from "@interfaces/image-carousel.ts"; import type { carouselGroup } from "@interfaces/image-carousel.ts";
import type { categorySkills } from "@interfaces/skill-matrix.ts";
import type { timelineEntry } from "@interfaces/timeline.ts"; import type { timelineEntry } from "@interfaces/timeline.ts";
import electronics_box from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/electronics-box.jpg"; import electronics_box from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/electronics-box.jpg";
import jet_drive from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/jet-drive.jpg"; import jet_drive from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/jet-drive.jpg";
import ross_ebox_4p0 from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/ross-ebox-4p0.jpg";
import ross_on_vessel_at_night from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/ross-on-vessel-at-night.jpg"; import ross_on_vessel_at_night from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/ross-on-vessel-at-night.jpg";
import ross_on_vessel from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/ross-on-vessel.jpg"; import ross_on_vessel from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/ross-on-vessel.jpg";
import publication from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/ross-publication.pdf"; import publication from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/ross-publication.pdf";
@@ -31,6 +40,7 @@ const headerCarouselGroup: carouselGroup = {
ross_team, ross_team,
ross_on_vessel, ross_on_vessel,
ross_on_vessel_at_night, ross_on_vessel_at_night,
ross_ebox_4p0,
electronics_box, electronics_box,
jet_drive, jet_drive,
ui, ui,
@@ -50,6 +60,72 @@ const timeline: timelineEntry[] = [
date: "May 2018", date: "May 2018",
}, },
]; ];
const categorizedSkills: categorySkills[] = [
{
category: "Electrical",
skills: [
{
item: "Schematic & PCB Design",
subItems: [{ item: "Altium Designer" }],
},
{
item: "PCB Assembly & Rework",
subItems: [
{ item: "Handheld Soldering" },
{ item: "Handheld Hot-Air Reflow" },
{ item: "Oven Reflow" },
],
},
{
item: "Electrical Diagnostics",
subItems: [
{ item: "Multimeters" },
{ item: "Oscilloscopes" },
{ item: "Logic Analyzers" },
],
},
{
item: "Harnessing Fabrication",
subItems: [
{ item: "DC Power & Signal" },
{ item: "Low Frequency RF (<1GHz)" },
{ item: "Waterproofing" },
],
},
{
item: "Simulation",
subItems: [{ item: "LTspice" }],
},
],
},
{
category: "Software & Environments",
skills: [
{ item: "Git" },
{
item: "Programming Languages",
subItems: [
{ item: "Python 2/3" },
{ item: "Bash Shell Scripting" },
{ item: "Low-Level Embedded C/C++ (Atmel Studio)" },
{ item: "High-Level Embedded C/C++ (Arduino/Teensy)" },
{ item: "Matlab" },
],
},
{
item: "Operating Systems",
subItems: [
{
item: "Linux",
subItems: [{ item: "Ubuntu" }, { item: "Raspbian" }],
},
{ item: "Microsoft Windows" },
],
},
],
},
];
--- ---
<ExperienceLayout <ExperienceLayout
@@ -57,56 +133,158 @@ const timeline: timelineEntry[] = [
subTitles={subTitles} subTitles={subTitles}
> >
<Carousel carouselGroup={headerCarouselGroup} /> <Carousel carouselGroup={headerCarouselGroup} />
<div class="mt-4 flex items-center justify-center"> <div class="grid grid-flow-row place-content-center gap-4 md:grid-flow-col">
<LinkButton <LinkButton
href="https://tos.org/oceanography/article/autonomous-ctd-profiling-from-the-robotic-oceanographic-surface-sampler" href="https://tos.org/oceanography/article/autonomous-ctd-profiling-from-the-robotic-oceanographic-surface-sampler"
title="Official Scientific Publication" title="Official Scientific Publication"
/> />
<LinkButton
href="https://www.kfsk.org/2017/04/19/remote-controlled-kayaks-ready-research-leconte-glacier/"
title="KFSK Petersburg Feature / Interview"
/>
</div> </div>
<H2>Summary</H2> <PageGroup>
<H3>Timeline</H3> <Fragment slot="header"><H2>Summary</H2></Fragment>
<Timeline timeline={timeline} /> <PageGroup>
<H3>Key Takeaways</H3> <Fragment slot="header"><H3>Timeline</H3></Fragment>
<ul class="list-inside list-disc"> <Timeline timeline={timeline} />
<li> </PageGroup>
<div class="inline-block"> <PageGroup>
Assembled, fabricated, and debugged both custom and <Fragment slot="header"><H3>Key Takeaways</H3></Fragment>
<PopoverWordDefinition key="COTS" /> <Ul>
hardware and electronics. <Li
</div> >Hand assembled and validated dozens of custom <PopoverWordDefinition
</li> key="PCBs"
<li>Two</li> />, wiring harnesses, and electronics boxes</Li
<li>Three</li> >
</ul> <Li
<h3 class="my-4 font-bold md:text-lg">Skills Used</h3> >Wrote, debugged, and assisted with development of embedded firmware</Li
<div >
class="border-caperren-green relative grid grid-flow-row gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4" <Li
> >Accompanied the team on two deployments to the LeConte glacier in
<div> Alaska to gather melt and mixing data</Li
<div class="text-sm font-extrabold">Software</div> >
<hr class="text-caperren-green" /> </Ul>
<ul class="list-inside list-disc text-sm"> </PageGroup>
<li>One</li> <SkillMatrix categorizedSkills={categorizedSkills} />
<li>Two</li> </PageGroup>
</ul> <PageGroup>
<Fragment slot="header"><H2>Details</H2></Fragment>
<PageGroup>
<Fragment slot="header"><H3>ROSS Overview</H3></Fragment>
<Paragraphs>
<Paragraph>
ROSS was a gasoline-powered water-sampling robotics platform built
around a Mokai jet-drive kayak. It's purpose was to continuously, and
often autonomously, gather water data over extremely long distances
and/or in locations where human-safety concerns would make gathering
it manually too risky. There were a variety of sensors it could be
outfitted with depending on the needs of the exact research project
and destination, but some common ones were an <PopoverWordDefinition
key="ADCP"
/> for gathering 3D water current vector data, a <PopoverWordDefinition
key="CTD"
/> for measuring water conductivity/temperature/depth, and a high-precision
GPS for generating meaningful 3D plots of the sensor data. These kayaks
have been deployed to places like the Indian/Pacific Ocean mixing line,
and along the active LeConte glacier terminus in Alaska, gathering novel
data on how vastly different bodies of water act when mixing.
</Paragraph>
<Paragraph>
In its original configuration, the Mokai kayak's throttle and steering
were already drive-by-wire, which made it an excellent starting point
for automating. It was also designed for easy transport, breaking down
into three major compartments that could easily fit in the back of a
short-bed pickup. For our custom hulls, Mokai also thickened the
plastic significantly and provided a bare minimum of electronics. This
barebones platform was then modified by our team to include a
storm-surge-rated intake and exhaust for the engine, a keel to improve
rough sea stability, a large alternator, and a plethora of mount
points the electronics, batteries, fuel, sensors, and radios.
</Paragraph>
<Paragraph>
In terms of the electronics and software for this project, the kayak
itself was centered around a Pixhawk flight controller flashed with a
modified Rover variant (this was before a dedicated boat option
existed). One pelican-case electronics box housed this controller, a
small <PopoverWordDefinition key="NUC" /> with <PopoverWordDefinition
key="UPS"
/>, wifi router, radio control receiver, satellite modem, and quite a
few custom <PopoverWordDefinition key="PCBs" /> for interfacing with external
electronics and implementing glue logic/safety overrides. A second box housed
nothing but sealed lead-acid batteries, which were charged by the alternator
on later revisions of the platform. The PC ran a custom python script, which
interfaced with a Matlab GUI over a remote radio link. The kayak could also
be overridden with an FrSky RC controller, when at close range, and additionally
allowed for direct control without the PC needing to be in-the-loop. To
see some of the custom hardware inside of these boxes, check out Nick McComb's
design pages for them
<InlineLink
href="https://nickmccomb.net/college/printed-circuit-boards#omg"
>here</InlineLink
>! For even more context on ROSS, and history from before I joined the
project, check out his <InlineLink
href="https://nickmccomb.net/college/ross">summary page</InlineLink
>.
</Paragraph>
</Paragraphs>
</PageGroup>
<PageGroup>
<Fragment slot="header"><H3>My Experience</H3></Fragment>
<Paragraphs>
<Paragraph>
I first started on this project by doing what I thought was a one-off
help session for Nick, working on an issue he was having getting
ROSS's engine to start and shut down properly. I had more experience
with engines, and engine control, so I quickly realized that a beefier
and high-voltage-rated relay was needed to avoid arc-welding the
contacts closed during shutdown. He rolled out a <InlineLink
href="https://nickmccomb.net/college/printed-circuit-boards/ross-ebox-auxillary"
>new board revision</InlineLink
> with those changes and it was the final version used for the rest of ROSS's
lifetime. This little taste of the project, and some wishful prodding from
Nick, was enough for me to join the team part-time.
</Paragraph>
<Paragraph>
While the original plan for me was to re-write the <PopoverWordDefinition
key="GUI"
/> for ROSS in Python using Qt, it turns out they needed my help on the
electrical and firmware side more than anything, so most of my time at the
lab was focused on that. I hand-assembled so many of Nick's circuit boards
at this lab that I still can pick his out of a lot from design aesthetic
alone! I also helped with plenty of wiring harness builds, electrical box
fabrication, embedded firmware development, and of course, plenty of electrical
and software debugging. One thing that this project taught me very quickly
was how difficult it was to make reliable hardware in a high vibration,
electrically noisy, and salt-laden environments. The number of PCBs we went
through, alongside wiring harnesses, was pretty incredible considering the
lengths we went to in order to protect them.
</Paragraph>
<Paragraph>
A very unique aspect of this team/project, and a large part of why I
was drawn to it, was that it was about as hands-on as you could
possibly get. Doubly so for an undergraduate student! Not only did I
get to design and repair a real robot, but it was actually being used
for proper scientific research! We would regularly go to Newport, OR
for testing and have to make crazy additions and repairs on the fly.
This got even more extreme during my deployments to the <InlineLink
href="http://localhost:4321/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler"
>LeConte glacier</InlineLink
>, as you had to get creative and fix things with what you had on-hand
due to how remote we were. These are experiences that graduate
students rarely even get to have, so I'm extremely thankful and fond
of the time I spent here. Huge shout out to <InlineLink
href="https://nickmccomb.net">Nick</InlineLink
>, again, who made it possible in the first place! Also be sure to
check out the scientific paper on this project below!
</Paragraph>
</Paragraphs>
</PageGroup>
</PageGroup>
<PageGroup>
<Fragment slot="header"><H2>Official Scientific Publication</H2></Fragment>
<div class="h-334">
<PdfViewer pdf={publication} />
</div> </div>
<div> </PageGroup>
<div class="text-sm font-extrabold">Electrical</div>
<hr class="text-caperren-green" />
</div>
<div>
<div class="text-sm font-extrabold">Mechanical</div>
<hr class="text-caperren-green" />
</div>
<div>
<div class="text-sm font-extrabold">Other</div>
<hr class="text-caperren-green" />
</div>
</div>
<H2>Details</H2>
power and voltage logging
<H2>Official Scientific Publication</H2>
<div class="h-334">
<PdfViewer pdf={publication} />
</div>
</ExperienceLayout> </ExperienceLayout>

View File

@@ -5,6 +5,7 @@ import H2 from "@components/H2.astro";
import H3 from "@components/H3.astro"; import H3 from "@components/H3.astro";
import InlineLink from "@components/InlineLink.astro"; import InlineLink from "@components/InlineLink.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro"; import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import PageGroup from "@components/PageGroup.astro";
import Paragraph from "@components/Paragraph.astro"; import Paragraph from "@components/Paragraph.astro";
import Paragraphs from "@components/Paragraphs.astro"; import Paragraphs from "@components/Paragraphs.astro";
@@ -27,42 +28,49 @@ const rfidImplantCarouselGroup: carouselGroup = {
--- ---
<HobbyLayout title="Body Mods"> <HobbyLayout title="Body Mods">
<H2>RFID Implant</H2> <PageGroup>
<Carousel carouselGroup={rfidImplantCarouselGroup} /> <Fragment slot="header"><H2>RFID Implant</H2></Fragment>
<H3>Details</H3>
<Paragraphs> <Carousel carouselGroup={rfidImplantCarouselGroup} />
<Paragraph> <PageGroup>
Back when I was in college, a few of my friends and I got this crazy idea <Fragment slot="header"><H3>Details</H3></Fragment>
to all get RFID implants together. They are essentially the same things
you'd use to microchip a pet, but with a slightly different firmware <Paragraphs>
configuration, allowing scans with any 125KHz-compatible reader. The <Paragraph>
implants came from <InlineLink Back when I was in college, a few of my friends and I got this crazy
href="https://dangerousthings.com/product/xem/" idea to all get RFID implants together. They are essentially the same
>dangerousthings.com</InlineLink things you'd use to microchip a pet, but with a slightly different
>, and we were lucky enough to have a vet-med student as a friend who made firmware configuration, allowing scans with any 125KHz-compatible
the installation a quick and painless process! I'm glad that I'm not reader. The implants came from <InlineLink
afraid of needles, as the 16 gauge injector the kit came with was nothing href="https://dangerousthings.com/product/xem/"
to scoff at. Since healing, you would never know the implant was there, >dangerousthings.com</InlineLink
with the site leaving no scar or visible indication of its presence. >, and we were lucky enough to have a vet-med student as a friend who
</Paragraph> made the installation a quick and painless process! I'm glad that I'm
<Paragraph> not afraid of needles, as the 16 gauge injector the kit came with was
With that out of the way, our group began work on hardware which would nothing to scoff at. Since healing, you would never know the implant
support the new implants. The goal was to have a generic usb-keyboard was there, with the site leaving no scar or visible indication of its
emulator for typing passwords with a valid scan, a car off-acc-on ignition presence.
replacement, and a fairly specialized modification to the OSU Robotics </Paragraph>
Club's doorway scanning system so they would support these on top of the <Paragraph>
official OSU ID cards. As tends to happen, life got busy, and only the With that out of the way, our group began work on hardware which would
usb-keyboard emulator actually came to fruition. The electronics and support the new implants. The goal was to have a generic usb-keyboard
primary firmware were handled by <InlineLink href="https://nickmccomb.net" emulator for typing passwords with a valid scan, a car off-acc-on
>Nick McComb</InlineLink ignition replacement, and a fairly specialized modification to the OSU
>, enclosure by <InlineLink href="https://dylanthrush.com" Robotics Club's doorway scanning system so they would support these on
>Dylan Thrush</InlineLink top of the official OSU ID cards. As tends to happen, life got busy,
>, and I supported some minor firmware development and debugging. If you and only the usb-keyboard emulator actually came to fruition. The
want to see an example of the keyboard emulator unlocking a PC, check out electronics and primary firmware were handled by <InlineLink
the video on <InlineLink href="https://nickmccomb.net">Nick McComb</InlineLink
href="https://nickmccomb.net/college/printed-circuit-boards/computer-access-module" >, enclosure by <InlineLink href="https://dylanthrush.com"
>Nick's website</InlineLink >Dylan Thrush</InlineLink
>! >, and I supported some minor firmware development and debugging. If
</Paragraph> you want to see an example of the keyboard emulator unlocking a PC,
</Paragraphs> check out the video on <InlineLink
href="https://nickmccomb.net/college/printed-circuit-boards/computer-access-module"
>Nick's website</InlineLink
>!
</Paragraph>
</Paragraphs>
</PageGroup>
</PageGroup>
</HobbyLayout> </HobbyLayout>

View File

@@ -1,7 +1,12 @@
--- ---
import HobbyLayout from "@layouts/HobbyLayout.astro";
import H2 from "@components/H2.astro";
import LinkButton from "@components/LinkButton.astro"; import LinkButton from "@components/LinkButton.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro"; import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import HobbyLayout from "@layouts/HobbyLayout.astro"; import PageGroup from "@components/PageGroup.astro";
import Paragraph from "@components/Paragraph.astro";
import Paragraphs from "@components/Paragraphs.astro";
import type { carouselGroup } from "@interfaces/image-carousel.ts"; import type { carouselGroup } from "@interfaces/image-carousel.ts";
@@ -31,44 +36,53 @@ const headerCarouselGroup: carouselGroup = {
}; };
--- ---
<HobbyLayout title="Motorcycling - Chubby Buttons 2 Mount"> <HobbyLayout
title="Chubby Buttons 2 Mount"
subTitles={["Hobbies", "Motorcycling"]}
>
<Carousel carouselGroup={headerCarouselGroup} /> <Carousel carouselGroup={headerCarouselGroup} />
<div class="mt-4 flex items-center justify-center"> <div class="flex items-center justify-center">
<LinkButton <LinkButton
href="https://cad.onshape.com/documents/816b0b1bef7883d4dc25c66c/v/e11fe68753e080b72015cfb8/e/3802abbd9d7b7c4d2c7ebad3" href="https://cad.onshape.com/documents/816b0b1bef7883d4dc25c66c/v/e11fe68753e080b72015cfb8/e/3802abbd9d7b7c4d2c7ebad3"
title="Onshape CAD Design Files" title="Onshape CAD Design Files"
/> />
</div> </div>
<p class="mt-4"> <PageGroup>
Having ridden motorcycles since I was sixteen, and being an avid music <Fragment slot="header"><H2>Details</H2></Fragment>
enjoyer, I'd been looking for a way to improve my music listening experience <Paragraphs>
while on-the-go. One large pain-point I'd always had was with controlling <Paragraph>
track selection and volume levels while my gloves were on, as smartphones Having ridden motorcycles since I was sixteen, and being an avid music
don't respond very well to this, if at all. In 2023 I found out about chubby enjoyer, I'd been looking for a way to improve my music listening
buttons, a low-power and highly water-resistant media controller experience while riding. One large pain-point I'd always had was
specifically designed for use with gloves! The only problem was that it was controlling track selection and volume levels while my gloves were on,
designed to be worn on your arm using a strap, which isn't very practical on as smartphones don't respond very well to this, if at all. In 2023 I
a motorcycle. found out about chubby buttons, a low-power and highly water-resistant
</p> media controller specifically designed for use with gloves! The only
<p class="mt-4"> problem was that it was designed to be worn on your arm using a strap,
When starting this project, I'd recently gotten a 3D Printer, so having some which isn't very practical on a motorcycle.
baseline modelling skills I took some measurements, and began designing a </Paragraph>
proper mount. I already owned and used many 1" RAM compatible mounts and <Paragraph>
gear on my bikes, so I decided to make this one natively support the ball When starting this project, I'd recently gotten a 3D printer, so having
size to use an existing clamp I had stored away. This design was the first some baseline modelling skills I took some measurements, and began
where I decided to use heat-set inserts in the plastic, along with some designing a proper mount. I already owned and used many 1" RAM
medium-strength Loctite on the fasteners, due to the high-vibration compatible mounts and gear on my bikes, so I decided to make this one
environment the mount would see. The print was also done using a UV natively support the ball size to use an existing clamp I had stored
resistant, high-temp rated, and non-water-absorbing ASA filament, as the away. This design was the first where I decided to use heat-set inserts
direct expose to the elements would not allow something like cheap PLA to in the plastic, along with some medium-strength Loctite on the
last very long. fasteners, due to the high-vibration environment the mount would see.
</p> The print was also done using a UV resistant, high-temp rated, and
<p class="mt-4"> non-water-absorbing ASA filament, as the direct exposure to the elements
While my first iteration was sized appropriately and went together with no would not allow something like cheap PLA to last very long.
issues, the ball mount neck ended up snapping due to a low infill </Paragraph>
percentage. After changing that area to 100% infill, including a handful of <Paragraph>
the rear mount layers that it attached to, a second iteration has worked While my first iteration was sized appropriately and went together with
perfectly for a few years now! If you're interested in printing this no issues, the ball mount neck ended up snapping due to a low infill
yourself, feel free to download the model using the button under the photos! percentage. After changing that area to 100% infill, including a handful
</p> of the layers at the rear mounting face where the neck attaches, a
second iteration has worked perfectly for a few years now! If you're
interested in printing this yourself, feel free to download the model
using the button under the photos!
</Paragraph>
</Paragraphs>
</PageGroup>
</HobbyLayout> </HobbyLayout>

View File

@@ -4,6 +4,7 @@ import HobbyLayout from "@layouts/HobbyLayout.astro";
import H2 from "@components/H2.astro"; import H2 from "@components/H2.astro";
import H3 from "@components/H3.astro"; import H3 from "@components/H3.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro"; import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import PageGroup from "@components/PageGroup.astro";
import type { carouselGroup } from "@interfaces/image-carousel.ts"; import type { carouselGroup } from "@interfaces/image-carousel.ts";
@@ -39,26 +40,35 @@ const kz750CarouselGroup: carouselGroup = {
}; };
--- ---
<HobbyLayout title="Motorcycling - Lineup"> <HobbyLayout title="Lineup" subTitles={["Hobbies", "Motorcycling"]}>
<H2>Current Lineup</H2> <PageGroup>
<H3>2015 Yamaha FJR 1300</H3> <Fragment slot="header"><H2>Current Lineup</H2></Fragment>
<Carousel carouselGroup={fjrCarouselGroup} /> <PageGroup>
<Fragment slot="header"><H3>2015 Yamaha FJR 1300</H3></Fragment>
<H3>2021 CSC SG400</H3> <Carousel carouselGroup={fjrCarouselGroup} />
<Carousel carouselGroup={cscCarouselGroup} /> </PageGroup>
<PageGroup>
<H2>Prior Lineup</H2> <Fragment slot="header"><H3>2021 CSC SG400</H3></Fragment>
<H3>2005 Suzuki DRZ 400</H3> <Carousel carouselGroup={cscCarouselGroup} />
<Carousel carouselGroup={drzCarouselGroup} /> </PageGroup>
</PageGroup>
<h3 class="my-4 font-bold underline md:text-lg"> <PageGroup>
1991 Kawasaki Concours ZG1000 <Fragment slot="header"><H2>Prior Lineup</H2></Fragment>
</h3> <PageGroup>
<Carousel carouselGroup={concoursCarouselGroup} /> <Fragment slot="header"><H3>2005 Suzuki DRZ 400</H3></Fragment>
<Carousel carouselGroup={drzCarouselGroup} />
<H3>1979 Kawasaki KZ750</H3> </PageGroup>
<Carousel carouselGroup={kz750CarouselGroup} /> <PageGroup>
<Fragment slot="header"><H3>1991 Kawasaki Concours ZG1000</H3></Fragment>
<H3>1991 Kawasaki Ninja 600R</H3> <Carousel carouselGroup={concoursCarouselGroup} />
<Carousel carouselGroup={ninjaCarouselGroup} /> </PageGroup>
<PageGroup>
<Fragment slot="header"><H3>1979 Kawasaki KZ750</H3></Fragment>
<Carousel carouselGroup={kz750CarouselGroup} />
</PageGroup>
<PageGroup>
<Fragment slot="header"><H3>1991 Kawasaki Ninja 600R</H3></Fragment>
<Carousel carouselGroup={ninjaCarouselGroup} />
</PageGroup>
</PageGroup>
</HobbyLayout> </HobbyLayout>

View File

@@ -4,6 +4,7 @@ import BaseLayout from "@layouts/BaseLayout.astro";
import H2 from "@components/H2.astro"; import H2 from "@components/H2.astro";
import InlineLink from "@components/InlineLink.astro"; import InlineLink from "@components/InlineLink.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro"; import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import PageGroup from "@components/PageGroup.astro";
import Paragraph from "@components/Paragraph.astro"; import Paragraph from "@components/Paragraph.astro";
import Paragraphs from "@components/Paragraphs.astro"; import Paragraphs from "@components/Paragraphs.astro";
@@ -21,76 +22,81 @@ const headerCarouselGroup: carouselGroup = {
<BaseLayout title="About" showTitle={false}> <BaseLayout title="About" showTitle={false}>
<Carousel carouselGroup={headerCarouselGroup} /> <Carousel carouselGroup={headerCarouselGroup} />
<H2>Who Am I</H2> <PageGroup>
<Paragraphs> <Fragment slot="header"><H2>Who Am I</H2></Fragment>
<Paragraph> <Paragraphs>
My name is Corwin Perren, and I'm a multi-disciplinary engineer with a <InlineLink <Paragraph>
href="/education">degree in computer science</InlineLink My name is Corwin Perren, and I'm a multi-disciplinary engineer with a <InlineLink
> from Oregon State University. For as long as I can remember, I've been fascinated href="/education">degree in computer science</InlineLink
by how things work, never being shy about taking them apart to learn the gritty > from Oregon State University. For as long as I can remember, I've been fascinated
details. At a young age, I began tinkering, adding lights and fans and doorbells by how things work, never being shy about taking them apart to learn the gritty
to the pretend cardboard box houses my brother and I would play in. Later, I details. At a young age, I began tinkering, adding lights and fans and doorbells
learned to solder, work on vehicles and engines, install and run Linux, manage to the pretend cardboard box houses my brother and I would play in. Later,
enterprise computing infrastructure, build and repair computers, write scripts, I learned to solder, work on vehicles and engines, install and run Linux,
and by the end of high school set out with a clear goal for my college years. manage enterprise computing infrastructure, build and repair computers, write
I wanted to learn and teach myself enough to be able to think up almost any scripts, and by the end of high school set out with a clear goal for my college
project, encompassing all facets of engineering, and be capable of driving it years. I wanted to learn and teach myself enough to be able to think up almost
to completion with my own skill set. any project, encompassing all facets of engineering, and be capable of driving
</Paragraph> it to completion with my own skill set.
<Paragraph> </Paragraph>
I think young me would be very pleased by how well I managed to achieve <Paragraph>
that goal! Through college, I learned electronics and PCB design, embedded I think young me would be very pleased by how well I managed to achieve
and pc programming, basic mechanical design and fabrication, on top of that goal! Through college, I learned electronics and PCB design,
learning how to work well with others in a team. I quickly realized that embedded and pc programming, basic mechanical design and fabrication, on
robotics was an ideal focus due to its inherent multi-disciplinary nature, top of learning how to work well with others in a team. I quickly
and joined the OSU Robotics Club, which introduced me to people who are realized that robotics was an ideal focus due to its inherent
still my best friends today. Through student engineering jobs, I had the multi-disciplinary nature, and joined the OSU Robotics Club, which
unique opportunity to work on some incredible projects such as the introduced me to people who are still my best friends today. Through
<InlineLink student engineering jobs, I had the unique opportunity to work on some
href="/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler" incredible projects such as the
>robotic oceanographic surface sampler</InlineLink <InlineLink
> href="/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler"
and an <InlineLink >robotic oceanographic surface sampler</InlineLink
href="/experience/osu-sinnhuber-aquatic-research-laboratory/zebrafish-embryo-pick-and-plate" >
>embryo pick-and-plate machine</InlineLink and an <InlineLink
>. One my my proudest moments was when our club's mars rover took first href="/experience/osu-sinnhuber-aquatic-research-laboratory/zebrafish-embryo-pick-and-plate"
place at the Candian International Rover Challenge in 2018, for which I >embryo pick-and-plate machine</InlineLink
was the <InlineLink >. One my my proudest moments was when our club's mars rover took first
href="/experience/osu-robotics-club/mars-rover-software-team-lead" place at the Candian International Rover Challenge in 2018, for which I
>software lead</InlineLink was the <InlineLink
>! href="/experience/osu-robotics-club/mars-rover-software-team-lead"
</Paragraph> >software lead</InlineLink
<Paragraph> >!
After a short three-month <InlineLink </Paragraph>
href="/experience/spacex/avionics-test-engineering-internship" <Paragraph>
>internship</InlineLink After a short three-month <InlineLink
> at SpaceX in Hawthorne at the end of college, I applied for a <InlineLink href="/experience/spacex/avionics-test-engineering-internship"
href="/experience/spacex/hardware-test-engineer-i-ii" >internship</InlineLink
>test engineering</InlineLink > at SpaceX in Hawthorne at the end of college, I applied for a <InlineLink
> position with the company's Starlink team and was hired in mid-2019. For six href="/experience/spacex/hardware-test-engineer-i-ii"
years, I developed test system hardware, software, harnesses, mechanical fixtures, >test engineering</InlineLink
devops infrastructure, websites, and tooling to ensure that Starlink, Falcon, > position with the company's Starlink team and was hired in mid-2019. For
Dragon, and Starship component tests were producing well-validated and reliable six years, I developed test system hardware, software, harnesses, mechanical
hardware. Through it all, I got to apply and hone every skill I had developed, fixtures, devops infrastructure, websites, and tooling to ensure that Starlink,
while learning countless more. Now though, it's on to the next adventure, whatever Falcon, Dragon, and Starship component tests were producing well-validated
that may be! and reliable hardware. Through it all, I got to apply and hone every skill
</Paragraph> I had developed, while learning countless more. Now though, it's on to the
<Paragraph> next adventure, whatever that may be!
To learn more about my experiences, hobbies, interests, and skills, feel </Paragraph>
free to explore the site! While the short summary above provides some <Paragraph>
insight into who I am, it leaves out plenty! For example, I've been an To learn more about my experiences, hobbies, interests, and skills, feel
avid <InlineLink href="/hobby/motorcycling/lineup" free to explore the site! While the short summary above provides some
>motorcycle rider</InlineLink insight into who I am, it leaves out plenty! For example, I've been an
> since I was sixteen, and have an <InlineLink href="/hobby/body-mods" avid <InlineLink href="/hobby/motorcycling/lineup"
>rfid implant</InlineLink >motorcycle rider</InlineLink
> in my hand! > since I was sixteen, and have an <InlineLink href="/hobby/body-mods"
</Paragraph> >rfid implant</InlineLink
<Paragraph> > in my hand!
If you're interested in contacting me, feel free to message on <InlineLink </Paragraph>
href="https://github.com/caperren">LinkedIn</InlineLink <Paragraph class="mt-8 flex flex-col items-center" initialTab={false}>
>, or via the primary contact methods on my <InlineLink <div>
href="/resume/2025-11-10-infrastructure-engineer">resume</InlineLink If you're interested in contacting me, feel free to message on <InlineLink
>. href="https://github.com/caperren">LinkedIn</InlineLink
</Paragraph> >, or via the primary contact methods on my <InlineLink
</Paragraphs> href="/resume/2025-11-10-infrastructure-engineer">resume</InlineLink
>.
</div>
</Paragraph>
</Paragraphs>
</PageGroup>
</BaseLayout> </BaseLayout>