Skip to content

Commit

Permalink
Implement magnetometer toggle for firmware (#1114)
Browse files Browse the repository at this point in the history
  • Loading branch information
ImUrX committed Nov 4, 2024
1 parent 3473a45 commit 4c54fcf
Show file tree
Hide file tree
Showing 21 changed files with 596 additions and 57 deletions.
18 changes: 18 additions & 0 deletions gui/public/i18n/en/translation.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,12 @@ tracker-infos-hardware_identifier = Hardware ID
tracker-infos-imu = IMU Sensor
tracker-infos-board_type = Main board
tracker-infos-network_version = Protocol Version
tracker-infos-magnetometer = Magnetometer
tracker-infos-magnetometer-status = { $status ->
*[0] Not supported
[1] Disabled
[2] Enabled
}
## Tracker settings
tracker-settings-back = Go back to trackers list
Expand All @@ -200,6 +206,13 @@ tracker-settings-mounting_section-edit = Edit mounting
tracker-settings-drift_compensation_section = Allow drift compensation
tracker-settings-drift_compensation_section-description = Should this tracker compensate for its drift when drift compensation is enabled?
tracker-settings-drift_compensation_section-edit = Allow drift compensation
tracker-settings-use_mag = Allow magnetometer on this tracker
# Multiline!
tracker-settings-use_mag-description =
Should this tracker use magnetometer to reduce drift when magnetometer usage is allowed? <b>Please don't shutdown your tracker while toggling this!</b>
You need to allow magnetometer usage first, <magSetting>click here to go to the setting</magSetting>.
tracker-settings-use_mag-label = Allow magnetometer
# The .<name> means it's an attribute and it's related to the top key.
# In this case that is the settings for the assignment section.
tracker-settings-name_section = Tracker name
Expand Down Expand Up @@ -354,6 +367,11 @@ settings-general-tracker_mechanics-save_mounting_reset-description =
Saves the automatic mounting reset calibrations for the trackers between restarts. Useful
when wearing a suit where trackers don't move between sessions. <b>Not recommended for normal users!</b>
settings-general-tracker_mechanics-save_mounting_reset-enabled-label = Save mounting reset
settings-general-tracker_mechanics-use_mag_on_all_trackers = Use magnetometer on all IMU trackers that support it
settings-general-tracker_mechanics-use_mag_on_all_trackers-description =
Uses magnetometer on all trackers that have a compatible firmware for it, reducing drift in stable magnetic environments.
Can be disabled per tracker in the tracker's settings. <b>Please don't shutdown any of the trackers while toggling this!</b>
settings-general-tracker_mechanics-use_mag_on_all_trackers-label = Use magnetometer on trackers
## FK/Tracking settings
settings-general-fk_settings = Tracking settings
Expand Down
24 changes: 16 additions & 8 deletions gui/src/components/commons/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export function CheckBox({
control,
outlined,
name,
loading,
// input props
disabled,
...props
Expand All @@ -19,6 +20,7 @@ export function CheckBox({
variant?: 'checkbox' | 'toggle';
color?: 'primary' | 'secondary' | 'tertiary';
outlined?: boolean;
loading?: boolean;
} & React.HTMLProps<HTMLInputElement>) {
const classes = useMemo(() => {
const vriantsMap = {
Expand All @@ -32,7 +34,9 @@ export function CheckBox({
toggle: {
checkbox: classNames('hidden'),
toggle: classNames('w-10 h-4 rounded-full relative transition-colors'),
pin: classNames('h-2 w-2 bg-background-10 rounded-full absolute m-1'),
pin: classNames(
'h-2 w-2 bg-background-10 rounded-full absolute m-1 transition-opacity'
),
},
};
return vriantsMap[variant];
Expand Down Expand Up @@ -60,7 +64,8 @@ export function CheckBox({
'w-full py-3 flex gap-2 items-center text-standard-bold',
{
'px-3': outlined,
'cursor-pointer': !disabled,
'cursor-pointer': !disabled || !loading,
'cursor-default': disabled || loading,
}
)}
>
Expand All @@ -71,23 +76,26 @@ export function CheckBox({
name={name}
className={classes.checkbox}
type="checkbox"
disabled={disabled}
disabled={disabled || loading}
{...props}
/>
{variant === 'toggle' && (
<div
className={classNames(classes.toggle, {
'bg-accent-background-30': value && !disabled,
'bg-accent-background-30': value && !disabled && !loading,
'bg-accent-background-50': value && disabled,
'bg-accent-background-30 animate-pulse': loading && !disabled,
'bg-background-50':
(!value && color == 'primary') || color == 'secondary',
'bg-background-40': !value && color == 'tertiary',
((!value && color == 'primary') || color == 'secondary') &&
!loading,
'bg-background-40': !value && color == 'tertiary' && !loading,
})}
>
<div
className={classNames(classes.pin, {
'left-0': !value,
'right-0': value,
'left-0': !value && !loading,
'opacity-0': loading,
'right-0': value && !loading,
'bg-background-30': disabled,
})}
></div>
Expand Down
5 changes: 5 additions & 0 deletions gui/src/components/settings/pages/GeneralSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
SettingsPagePaneLayout,
} from '@/components/settings/SettingsPageLayout';
import { HandsWarningModal } from '@/components/settings/HandsWarningModal';
import { MagnetometerToggleSetting } from './MagnetometerToggleSetting';
import { DriftCompensationModal } from '@/components/settings/DriftCompensationModal';

interface SettingsForm {
Expand Down Expand Up @@ -808,6 +809,10 @@ export function GeneralSettings() {
'settings-general-tracker_mechanics-save_mounting_reset-enabled-label'
)}
/>
<MagnetometerToggleSetting
settingType="general"
id="mechanics-magnetometer"
/>
</>
</SettingsPagePaneLayout>
<SettingsPagePaneLayout
Expand Down
160 changes: 160 additions & 0 deletions gui/src/components/settings/pages/MagnetometerToggleSetting.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import { CheckBox } from '@/components/commons/Checkbox';
import { Typography } from '@/components/commons/Typography';
import { useWebsocketAPI } from '@/hooks/websocket-api';
import { Localized, useLocalization } from '@fluent/react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useForm } from 'react-hook-form';
import {
ChangeMagToggleRequestT,
DeviceIdT,
MagToggleRequestT,
MagToggleResponseT,
RpcMessage,
TrackerIdT,
} from 'solarxr-protocol';
import { Link } from 'react-router-dom';

interface MagnetometerToggleForm {
magToggle: boolean;
}

export function MagnetometerToggleSetting({
trackerNum,
deviceId,
settingType,
id,
}: {
trackerNum?: number;
deviceId?: number;
settingType: 'general' | 'tracker';
id?: string;
}) {
const { l10n } = useLocalization();
const { sendRPCPacket, useRPCPacket } = useWebsocketAPI();
const originalValue = useRef<boolean | null>(null);
// used to disable the tracker specific toggle if false
const [globalToggle, setGlobalToggle] = useState(false);
const [waitingMag, setWaitingMag] = useState(false);
const { control, watch, handleSubmit, reset } =
useForm<MagnetometerToggleForm>({
defaultValues: { magToggle: settingType === 'tracker' },
});

const onSubmit = useCallback(
(values: MagnetometerToggleForm) => {
if (originalValue.current === values.magToggle) return;
setWaitingMag(true);
const req = new ChangeMagToggleRequestT();
if (trackerNum !== undefined) {
const id = new TrackerIdT(
deviceId ? new DeviceIdT(deviceId) : undefined,
trackerNum
);
req.trackerId = id;
}

req.enable = values.magToggle;
sendRPCPacket(RpcMessage.ChangeMagToggleRequest, req);
},
[trackerNum, deviceId]
);

useEffect(() => {
const subscription = watch(() => handleSubmit(onSubmit)());
return () => subscription.unsubscribe();
}, []);

useEffect(() => {
const req = new MagToggleRequestT();
if (trackerNum !== undefined) {
const id = new TrackerIdT(
deviceId ? new DeviceIdT(deviceId) : undefined,
trackerNum
);
req.trackerId = id;
sendRPCPacket(RpcMessage.MagToggleRequest, new MagToggleRequestT());
}
sendRPCPacket(RpcMessage.MagToggleRequest, req);
}, [trackerNum, deviceId]);

useRPCPacket(RpcMessage.MagToggleResponse, (mag: MagToggleResponseT) => {
if (trackerNum !== undefined && mag.trackerId?.trackerNum === undefined) {
setGlobalToggle(mag.enable);
}
if (
mag.trackerId?.trackerNum !== trackerNum ||
mag.trackerId?.deviceId?.id !== deviceId
) {
return;
}
originalValue.current = mag.enable;
setWaitingMag(false);
reset({ magToggle: mag.enable });
});

return settingType === 'general' ? (
<>
<div className="flex flex-col pt-5 pb-3" id={id}>
<Typography bold>
{l10n.getString(
'settings-general-tracker_mechanics-use_mag_on_all_trackers'
)}
</Typography>
<Localized
id="settings-general-tracker_mechanics-use_mag_on_all_trackers-description"
elems={{ b: <b></b> }}
>
<Typography
color="secondary"
whitespace="whitespace-pre-line"
></Typography>
</Localized>
</div>
<CheckBox
variant="toggle"
outlined
control={control}
loading={waitingMag}
name="magToggle"
label={l10n.getString(
'settings-general-tracker_mechanics-use_mag_on_all_trackers-label'
)}
/>
</>
) : (
<div className="flex flex-col gap-2 w-full mt-3" id={id}>
<Typography variant="section-title">
{l10n.getString('tracker-settings-use_mag')}
</Typography>
<Localized
id="tracker-settings-use_mag-description"
elems={{
b: <b></b>,
magSetting: (
<Link
to="/settings/trackers"
state={{ scrollTo: 'mechanics-magnetometer' }}
className="underline"
></Link>
),
}}
>
<Typography
color="secondary"
whitespace="whitespace-pre-line"
></Typography>
</Localized>
<div className="flex">
<CheckBox
variant="toggle"
outlined
loading={waitingMag}
disabled={!globalToggle}
name="magToggle"
control={control}
label={l10n.getString('tracker-settings-use_mag-label')}
/>
</div>
</div>
);
}
20 changes: 20 additions & 0 deletions gui/src/components/tracker/TrackerSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { SingleTrackerBodyAssignmentMenu } from './SingleTrackerBodyAssignmentMe
import { TrackerCard } from './TrackerCard';
import { Quaternion } from 'three';
import { useAppContext } from '@/hooks/app';
import { MagnetometerToggleSetting } from '@/components/settings/pages/MagnetometerToggleSetting';

const rotationsLabels: [Quaternion, string][] = [
[rotationToQuatMap.BACK, 'tracker-rotation-back'],
Expand Down Expand Up @@ -276,6 +277,18 @@ export function TrackerSettingsPage() {
{tracker?.device?.hardwareInfo?.boardType || '--'}
</Typography>
</div>
<div className="flex justify-between">
<Typography color="secondary">
{l10n.getString('tracker-infos-magnetometer')}
</Typography>
<Typography>
{tracker?.tracker.info?.magnetometer === undefined
? '--'
: l10n.getString('tracker-infos-magnetometer-status', {
status: tracker.tracker.info.magnetometer,
})}
</Typography>
</div>
<div className="flex justify-between">
<Typography color="secondary">
{l10n.getString('tracker-infos-network_version')}
Expand Down Expand Up @@ -403,6 +416,13 @@ export function TrackerSettingsPage() {
</div>
</div>
)}
{tracker?.tracker.info?.isImu && (
<MagnetometerToggleSetting
settingType="tracker"
trackerNum={tracker.tracker.trackerId?.trackerNum}
deviceId={tracker.tracker.trackerId?.deviceId?.id}
/>
)}
<div className="flex flex-col gap-2 w-full mt-3">
<Typography variant="section-title">
{l10n.getString('tracker-settings-name_section')}
Expand Down
2 changes: 1 addition & 1 deletion server/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ configure<com.diffplug.gradle.spotless.SpotlessExtension> {
"java.util.*,kotlin.math.*,dev.slimevr.autobone.errors.*" +
",io.github.axisangles.ktmath.*,kotlinx.atomicfu.*" +
",dev.slimevr.tracking.trackers.*,dev.slimevr.desktop.platform.ProtobufMessages.*" +
",com.illposed.osc.*,android.app.*",
",solarxr_protocol.rpc.*,kotlinx.coroutines.*,com.illposed.osc.*,android.app.*",
"ij_kotlin_allow_trailing_comma" to true,
)
val ktlintVersion = "1.2.1"
Expand Down
1 change: 1 addition & 0 deletions server/core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ dependencies {
implementation("org.java-websocket:Java-WebSocket:1.+")
implementation("com.melloware:jintellitype:1.+")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1")

// Jitpack
implementation("com.github.SlimeVR:oscquery-kt:566a0cba58")
Expand Down
10 changes: 0 additions & 10 deletions server/core/src/main/java/dev/slimevr/config/ServerConfig.java

This file was deleted.

Loading

0 comments on commit 4c54fcf

Please sign in to comment.