Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/lib/lang/_template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export default o({
current_status: _,
mount: _,
image: _,
guide: _,
labels: o({
temp: _,
exposure: _,
Expand Down
1 change: 1 addition & 0 deletions src/lib/lang/czech.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export default lang.parse({
current_status: 'Aktuální stav',
mount: 'Montáž',
image: 'Poslední obrázek',
guide: 'Navádění',
labels: {
temp: 'Teplota',
exposure: 'Expozice',
Expand Down
1 change: 1 addition & 0 deletions src/lib/lang/english.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export default lang.parse({
current_status: 'Current Status',
mount: 'Mount',
image: 'Latest Image',
guide: 'Guiding',
labels: {
temp: 'Temperature',
exposure: 'Exposure',
Expand Down
64 changes: 61 additions & 3 deletions src/lib/server/nina.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,43 @@ interface SequenceItem {
Triggers?: SequenceItem[];
TargetTime?: string;
Delay?: number;
ExposureTime?: number;
}

interface CameraInfo {
ExposureEndTime: string;
IsExposing: boolean;
}

//{"Response":{"Connected":true,"Name":"PHD2","DisplayName":"PHD2","Description":"PHD2 Guider","DriverInfo":"PHD2 Guider","DriverVersion":"1.0","DeviceId":"PHD2_Single","CanClearCalibration":true,"CanSetShiftRate":true,"CanGetLockPosition":true,"SupportedActions":[],"RMSError":{"RA":{"Pixel":0.1670851110717076,"Arcseconds":0.5743968405867628},"Dec":{"Pixel":0.14570647415145718,"Arcseconds":0.5009024315141719},"Total":{"Pixel":0.22169305571328599,"Arcseconds":0.7621253022783488},"PeakRA":{"Pixel":0.52,"Arcseconds":1.78763},"PeakDec":{"Pixel":1.21,"Arcseconds":4.1596775}},"PixelScale":3.43775,"LastGuideStep":{"RADistanceRaw":0.12,"DECDistanceRaw":-0.088,"RADuration":1,"DECDuration":0},"State":"Guiding"},"Error":"","StatusCode":200,"Success":true,"Type":"API"}
interface GuideResponse {
RMSError: {
RA: {
Pixel: number;
Arcseconds: number;
};
Dec: {
Pixel: number;
Arcseconds: number;
};
Total: {
Pixel: number;
Arcseconds: number;
};
};
}

interface GuideInfo {
ra: number;
dec: number;
total: number;
}

export type LiveStatus = {
active: boolean;
imageInfo?: ImageHistoryItem;
mountInfo?: MountInfo;
guideInfo?: GuideInfo;
currentAction?: string;
showImage?: boolean;
};
Expand Down Expand Up @@ -83,7 +114,7 @@ export class NinaClient {
}
}

private mapRunningAction(item: SequenceItem): string {
private async mapRunningAction(item: SequenceItem): Promise<string> {
const name = item.Name || '';
const map: Record<string, string> = {
'Meridian Flip_Trigger': 'Meridian flip',
Expand All @@ -109,6 +140,22 @@ export class NinaClient {
}
} else if (name === 'Wait for Time Span' && item.Delay) {
action += ` for ${item.Delay}s`;
} else if (name === 'Smart Exposure' && item.ExposureTime) {
const time = item.ExposureTime;

//fetch camera end time:
const info = await this.fetch<NinaResponse<CameraInfo>>(
'api/equipment/camera/info'
);

//calculate XXs/XXs (calc/time)
if (info?.Success && info.Response.ExposureEndTime) {
const endTime = new Date(info.Response.ExposureEndTime).getTime();
const now = Date.now();
const elapsed = (now - (endTime - time * 1000)) / 1000;
const total = time;
action += ` (${elapsed.toFixed(0)}s/${total}s)`;
}
}

return action;
Expand Down Expand Up @@ -205,7 +252,7 @@ export class NinaClient {
this.lastUpdate = now;
}

const currentAction = this.mapRunningAction(deepestItem!);
const currentAction = await this.mapRunningAction(deepestItem!);
const hiddenActions = [
'Meridian flip',
'Waiting',
Expand All @@ -224,12 +271,23 @@ export class NinaClient {
showImage = false;
}

const guideData = await this.fetch<NinaResponse<GuideResponse>>(
'api/equipment/guider/info'
);

this.cachedLiveStatus = {
active: true,
mountInfo: mountData?.Response,
imageInfo,
currentAction,
showImage
showImage,
guideInfo: guideData?.Success
? {
ra: guideData.Response.RMSError.RA.Arcseconds,
dec: guideData.Response.RMSError.Dec.Arcseconds,
total: guideData.Response.RMSError.Total.Arcseconds
}
: undefined
};

return this.cachedLiveStatus;
Expand Down
167 changes: 98 additions & 69 deletions src/routes/[[lang=lang]]/live-photo/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,22 @@

<div class="mt-8 grid grid-cols-1 gap-6 px-2 lg:grid-cols-3">
<div class="lg:col-span-2">
{#if liveData?.active && liveData?.showImage}
<img
src={`/api/live-image?t=${now}`}
alt="Live view"
class="w-full rounded-lg shadow-lg"
/>
{:else}
<div
class="flex aspect-video w-full items-center justify-center rounded-lg bg-black text-xl text-white shadow-lg"
>
<div
class="relative flex aspect-video w-full items-center justify-center overflow-hidden rounded-lg bg-black text-xl text-white shadow-lg"
>
<span class="z-0 px-4 text-center">
{liveData?.active
? liveData?.currentAction || appState.lang.live_photo.inactive
: appState.lang.live_photo.inactive}
</div>
{/if}
</span>
{#if liveData?.active && liveData?.showImage}
<img
src={`/api/live-image?t=${now}`}
alt="Live view"
class="absolute inset-0 z-10 h-full w-full object-contain"
/>
{/if}
</div>
</div>

<div
Expand Down Expand Up @@ -99,73 +100,101 @@
</div>
</div>

<!-- Image Info Section -->
<!-- Guide Section -->
<div>
<h3 class="mb-2 border-b border-zinc-300 pb-1 font-semibold">
{appState.lang.live_photo.image}
{appState.lang.live_photo.guide} (PHD2)
</h3>
<div class="grid grid-cols-2 gap-2 text-sm">
<span class="text-secondary font-bold">
{appState.lang.live_photo.labels.target}:</span
>
<span class="truncate text-right" title={liveData?.imageInfo?.TargetName}>
{liveData?.imageInfo?.TargetName || 'No info'}</span
>

<span class="text-secondary font-bold">
{appState.lang.live_photo.labels.date}:</span
>
<span class="text-right">
{liveData?.imageInfo?.Date
? new Date(liveData.imageInfo.Date).toLocaleTimeString()
: '-'}</span
>

<span class="text-secondary font-bold">
{appState.lang.live_photo.labels.exposure}:</span
>
<span class="text-right">
{liveData?.imageInfo?.ExposureTime
? `${liveData.imageInfo.ExposureTime}s`
: '-'}</span
>

<span class="text-secondary font-bold">
{appState.lang.live_photo.labels.temp}:</span
>
<span class="text-right">
{liveData?.imageInfo?.Temperature
? `${liveData.imageInfo.Temperature}°C`
: '-'}</span
<div class="grid grid-cols-2 gap-2">
<span class="text-secondary font-bold"> RA Error:</span>
<span>
{liveData?.guideInfo ? `${liveData.guideInfo.ra.toFixed(2)}″"` : '-'}</span
>

<span class="text-secondary font-bold">
{appState.lang.live_photo.labels.gain}:</span
<span class="text-secondary font-bold"> Dec Error:</span>
<span>
{liveData?.guideInfo ? `${liveData.guideInfo.dec.toFixed(2)}″"` : '-'}</span
>
<span class="text-right"> {liveData?.imageInfo?.Gain ?? '-'}</span>

<span class="text-secondary font-bold">
{appState.lang.live_photo.labels.focal_length}:</span
>
<span class="text-right">
{liveData?.imageInfo?.FocalLength
? `${liveData.imageInfo.FocalLength}mm`
<span class="text-secondary font-bold"> Total Error:</span>
<span>
{liveData?.guideInfo
? `${liveData.guideInfo.total.toFixed(2)}″"`
: '-'}</span
>
</div>

<span class="text-secondary col-span-2 font-bold">
{appState.lang.live_photo.labels.telescope}:</span
>
<span class="col-span-2 truncate" title={liveData?.imageInfo?.TelescopeName}>
{liveData?.imageInfo?.TelescopeName || 'No info'}</span
>

<span class="text-secondary col-span-2 font-bold">
{appState.lang.live_photo.labels.camera}:</span
>
<span class="col-span-2 truncate" title={liveData?.imageInfo?.CameraName}>
{liveData?.imageInfo?.CameraName || 'No info'}</span
>
<!-- Image Info Section -->
<div>
<h3 class="mb-2 border-b border-zinc-300 pb-1 font-semibold">
{appState.lang.live_photo.image}
</h3>
<div class="grid grid-cols-2 gap-2 text-sm">
<span class="text-secondary font-bold">
{appState.lang.live_photo.labels.target}:</span
>
<span class="truncate text-right" title={liveData?.imageInfo?.TargetName}>
{liveData?.imageInfo?.TargetName || 'No info'}</span
>

<span class="text-secondary font-bold">
{appState.lang.live_photo.labels.date}:</span
>
<span class="text-right">
{liveData?.imageInfo?.Date
? new Date(liveData.imageInfo.Date).toLocaleTimeString()
: '-'}</span
>

<span class="text-secondary font-bold">
{appState.lang.live_photo.labels.exposure}:</span
>
<span class="text-right">
{liveData?.imageInfo?.ExposureTime
? `${liveData.imageInfo.ExposureTime}s`
: '-'}</span
>

<span class="text-secondary font-bold">
{appState.lang.live_photo.labels.temp}:</span
>
<span class="text-right">
{liveData?.imageInfo?.Temperature
? `${liveData.imageInfo.Temperature}°C`
: '-'}</span
>

<span class="text-secondary font-bold">
{appState.lang.live_photo.labels.gain}:</span
>
<span class="text-right"> {liveData?.imageInfo?.Gain ?? '-'}</span>

<span class="text-secondary font-bold">
{appState.lang.live_photo.labels.focal_length}:</span
>
<span class="text-right">
{liveData?.imageInfo?.FocalLength
? `${liveData.imageInfo.FocalLength}mm`
: '-'}</span
>

<span class="text-secondary col-span-2 font-bold">
{appState.lang.live_photo.labels.telescope}:</span
>
<span
class="col-span-2 truncate"
title={liveData?.imageInfo?.TelescopeName}
>
{liveData?.imageInfo?.TelescopeName || 'No info'}</span
>

<span class="text-secondary col-span-2 font-bold">
{appState.lang.live_photo.labels.camera}:</span
>
<span class="col-span-2 truncate" title={liveData?.imageInfo?.CameraName}>
{liveData?.imageInfo?.CameraName || 'No info'}</span
>
</div>
</div>
</div>
</div>
Expand Down