ascom_alpaca/api/camera/image_array/
mod.rs1#[cfg(feature = "client")]
2mod client;
3#[cfg(feature = "server")]
4mod server;
5
6#[cfg(feature = "server")]
7pub(crate) use server::ImageBytesResponse;
8
9use bytemuck::{AnyBitPattern, Pod, Zeroable};
10use ndarray::{Array2, Array3, ArrayView2, ArrayView3, Axis};
11use num_enum::{IntoPrimitive, TryFromPrimitive};
12use serde_repr::{Deserialize_repr, Serialize_repr};
13use std::num::NonZeroU32;
14
15type ArcArray3<T> = ndarray::ArcArray<T, ndarray::Ix3>;
17
18#[derive(
20 Debug,
21 PartialEq,
22 Eq,
23 Clone,
24 Copy,
25 Serialize_repr,
26 Deserialize_repr,
27 TryFromPrimitive,
28 IntoPrimitive,
29)]
30#[repr(i32)]
31pub enum ImageArrayRank {
32 Rank2 = 2_i32,
34 Rank3 = 3_i32,
36}
37
38#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
39#[repr(i32)]
40pub(crate) enum TransmissionElementType {
41 I16 = 1,
42 I32 = 2,
43 U8 = 6,
44 U16 = 8,
45}
46
47#[derive(
49 Debug,
50 PartialEq,
51 Eq,
52 Clone,
53 Copy,
54 Serialize_repr,
55 Deserialize_repr,
56 IntoPrimitive,
57 TryFromPrimitive,
58)]
59#[repr(i32)]
60pub(crate) enum ImageElementType {
61 I32 = 2,
63}
64
65trait AsTransmissionElementType: 'static + Into<i32> + AnyBitPattern {
66 const TYPE: TransmissionElementType;
67}
68
69impl AsTransmissionElementType for i16 {
70 const TYPE: TransmissionElementType = TransmissionElementType::I16;
71}
72
73impl AsTransmissionElementType for i32 {
74 const TYPE: TransmissionElementType = TransmissionElementType::I32;
75}
76
77impl AsTransmissionElementType for u16 {
78 const TYPE: TransmissionElementType = TransmissionElementType::U16;
79}
80
81impl AsTransmissionElementType for u8 {
82 const TYPE: TransmissionElementType = TransmissionElementType::U8;
83}
84
85#[derive(Debug, PartialEq, Eq, Clone, derive_more::Deref)]
94pub struct ImageArray {
95 #[deref]
96 data: ArcArray3<i32>,
97 transmission_element_type: TransmissionElementType,
98}
99
100const COLOUR_AXIS: Axis = Axis(2);
101
102impl<T: AsTransmissionElementType> From<ArrayView3<'_, T>> for ImageArray {
103 fn from(array: ArrayView3<'_, T>) -> Self {
104 let data = array.mapv(Into::into);
105 let transmission_element_type = T::TYPE;
106 Self {
107 data: data.into_shared(),
108 transmission_element_type,
109 }
110 }
111}
112
113impl<T: AsTransmissionElementType> From<Array3<T>> for ImageArray {
114 fn from(array: Array3<T>) -> Self {
115 let data = array.mapv_into_any(Into::into);
116 let transmission_element_type = T::TYPE;
117 Self {
118 data: data.into_shared(),
119 transmission_element_type,
120 }
121 }
122}
123
124impl<T: AsTransmissionElementType> From<ArrayView2<'_, T>> for ImageArray {
125 fn from(array: ArrayView2<'_, T>) -> Self {
126 array.insert_axis(COLOUR_AXIS).into()
127 }
128}
129
130impl<T: AsTransmissionElementType> From<Array2<T>> for ImageArray {
131 fn from(array: Array2<T>) -> Self {
132 array.insert_axis(COLOUR_AXIS).into()
133 }
134}
135
136impl ImageArray {
137 pub fn rank(&self) -> ImageArrayRank {
139 match self.data.len_of(COLOUR_AXIS) {
140 1 => ImageArrayRank::Rank2,
141 _ => ImageArrayRank::Rank3,
142 }
143 }
144}
145
146#[cfg(not(target_endian = "little"))]
147compile_error!(
148"Image handling is currently only supported on little-endian platforms for simplicity & performance.
149If you have a real-world use case for big-endian support, please open an issue on GitHub."
150);
151
152#[cfg(any(feature = "client", feature = "server"))]
153#[repr(C)]
154#[derive(Clone, Copy, Zeroable, Pod)]
155struct ImageBytesMetadata {
156 metadata_version: i32,
157 error_number: i32,
158 client_transaction_id: Option<NonZeroU32>,
159 server_transaction_id: Option<NonZeroU32>,
160 data_start: i32,
161 image_element_type: i32,
162 transmission_element_type: i32,
163 rank: i32,
164 dimension_1: i32,
165 dimension_2: i32,
166 dimension_3: i32,
167}
168
169const IMAGE_BYTES_TYPE: &str = "application/imagebytes";