Adding stream format, updating header format
This commit is contained in:
parent
6c87db3778
commit
8a143a3f58
|
@ -10,13 +10,15 @@ import struct
|
||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
# Representation of a single UDP packet transmitted by Stabilizer.
|
# Representation of a single data batch transmitted by Stabilizer.
|
||||||
Packet = collections.namedtuple('Packet', ['index', 'adc', 'dac'])
|
Packet = collections.namedtuple('Packet', ['index', 'data'])
|
||||||
|
|
||||||
Format = collections.namedtuple('Format', ['batch_size', 'length'])
|
Format = collections.namedtuple('Format', ['sample_size_bytes', 'batch_format'])
|
||||||
|
|
||||||
|
# All supported formats by this reception script.
|
||||||
FORMAT = {
|
FORMAT = {
|
||||||
0: Format(8, 964)
|
0: Format(sample_size_bytes=8,
|
||||||
|
batch_format='<{batch_size}H{batch_size}H{batch_size}H{batch_size}H')
|
||||||
}
|
}
|
||||||
|
|
||||||
class Timer:
|
class Timer:
|
||||||
|
@ -94,32 +96,25 @@ class PacketParser:
|
||||||
if len(self.buf) < 4:
|
if len(self.buf) < 4:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
start_id, format_id = struct.unpack_from('<HH', self.buf)
|
start_id, format_id, batch_count, batch_size = struct.unpack_from('<HHHB', self.buf)
|
||||||
|
|
||||||
|
if format_id not in FORMAT:
|
||||||
|
raise Exception(f'Unknown format specifier: {format_id}')
|
||||||
|
|
||||||
frame_format = FORMAT[format_id]
|
frame_format = FORMAT[format_id]
|
||||||
|
required_length = 7 + batch_count * frame_format.sample_size_bytes * batch_size
|
||||||
|
|
||||||
if len(self.buf) < frame_format.length:
|
if len(self.buf) < required_length:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
self.buf = self.buf[4:]
|
self.buf = self.buf[7:]
|
||||||
|
|
||||||
batches_per_frame = int((frame_format.length - 4) / (frame_format.batch_size * 8))
|
|
||||||
|
|
||||||
packets = []
|
packets = []
|
||||||
for offset in range(batches_per_frame):
|
for offset in range(batch_count):
|
||||||
adcs_dacs = struct.unpack_from(f'<{4 * frame_format.batch_size}H', self.buf)
|
format_string = frame_format.batch_format.format(batch_size=batch_size)
|
||||||
adc = [
|
data = struct.unpack_from(format_string, self.buf)
|
||||||
adcs_dacs[0:frame_format.batch_size],
|
self.buf = self.buf[struct.calcsize(format_string):]
|
||||||
adcs_dacs[frame_format.batch_size:2*frame_format.batch_size],
|
packets.append(Packet(start_id + offset, data))
|
||||||
]
|
|
||||||
|
|
||||||
dac = [
|
|
||||||
adcs_dacs[2*frame_format.batch_size: 3*frame_format.batch_size],
|
|
||||||
adcs_dacs[3*frame_format.batch_size:],
|
|
||||||
]
|
|
||||||
|
|
||||||
self.buf = self.buf[8*frame_format.batch_size:]
|
|
||||||
packets.append(Packet(start_id + offset, adc, dac))
|
|
||||||
|
|
||||||
return packets
|
return packets
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ use stabilizer::{
|
||||||
DigitalInput0, DigitalInput1, AFE0, AFE1,
|
DigitalInput0, DigitalInput1, AFE0, AFE1,
|
||||||
},
|
},
|
||||||
net::{
|
net::{
|
||||||
data_stream::{FrameGenerator, StreamTarget},
|
data_stream::{FrameGenerator, StreamFormat, StreamTarget},
|
||||||
miniconf::Miniconf,
|
miniconf::Miniconf,
|
||||||
serde::Deserialize,
|
serde::Deserialize,
|
||||||
telemetry::{Telemetry, TelemetryBuffer},
|
telemetry::{Telemetry, TelemetryBuffer},
|
||||||
|
@ -308,24 +308,43 @@ const APP: () = {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stream the data.
|
// Stream the data.
|
||||||
generator.add::<_, { SAMPLE_BUFFER_SIZE * 8 }>(0, |buf| unsafe {
|
generator.add::<_, { SAMPLE_BUFFER_SIZE * 8 }>(
|
||||||
|
StreamFormat::AdcDacData,
|
||||||
|
|buf| unsafe {
|
||||||
let dst = buf.as_ptr() as usize as *mut u16;
|
let dst = buf.as_ptr() as usize as *mut u16;
|
||||||
|
|
||||||
let adc0 = &adc_samples[0][0] as *const u16;
|
let adc0 = &adc_samples[0][0] as *const u16;
|
||||||
core::ptr::copy_nonoverlapping(adc0, dst, SAMPLE_BUFFER_SIZE);
|
core::ptr::copy_nonoverlapping(
|
||||||
|
adc0,
|
||||||
|
dst,
|
||||||
|
SAMPLE_BUFFER_SIZE,
|
||||||
|
);
|
||||||
|
|
||||||
let dst = dst.add(SAMPLE_BUFFER_SIZE);
|
let dst = dst.add(SAMPLE_BUFFER_SIZE);
|
||||||
let adc1 = &adc_samples[1][0] as *const u16;
|
let adc1 = &adc_samples[1][0] as *const u16;
|
||||||
core::ptr::copy_nonoverlapping(adc1, dst, SAMPLE_BUFFER_SIZE);
|
core::ptr::copy_nonoverlapping(
|
||||||
|
adc1,
|
||||||
|
dst,
|
||||||
|
SAMPLE_BUFFER_SIZE,
|
||||||
|
);
|
||||||
|
|
||||||
let dst = dst.add(SAMPLE_BUFFER_SIZE);
|
let dst = dst.add(SAMPLE_BUFFER_SIZE);
|
||||||
let dac0 = &dac_samples[0][0] as *const u16;
|
let dac0 = &dac_samples[0][0] as *const u16;
|
||||||
core::ptr::copy_nonoverlapping(dac0, dst, SAMPLE_BUFFER_SIZE);
|
core::ptr::copy_nonoverlapping(
|
||||||
|
dac0,
|
||||||
|
dst,
|
||||||
|
SAMPLE_BUFFER_SIZE,
|
||||||
|
);
|
||||||
|
|
||||||
let dst = dst.add(SAMPLE_BUFFER_SIZE);
|
let dst = dst.add(SAMPLE_BUFFER_SIZE);
|
||||||
let dac1 = &dac_samples[1][0] as *const u16;
|
let dac1 = &dac_samples[1][0] as *const u16;
|
||||||
core::ptr::copy_nonoverlapping(dac1, dst, SAMPLE_BUFFER_SIZE);
|
core::ptr::copy_nonoverlapping(
|
||||||
});
|
dac1,
|
||||||
|
dst,
|
||||||
|
SAMPLE_BUFFER_SIZE,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// Update telemetry measurements.
|
// Update telemetry measurements.
|
||||||
telemetry.adcs =
|
telemetry.adcs =
|
||||||
|
|
|
@ -52,7 +52,7 @@ use stabilizer::{
|
||||||
DigitalInput0, DigitalInput1, AFE0, AFE1,
|
DigitalInput0, DigitalInput1, AFE0, AFE1,
|
||||||
},
|
},
|
||||||
net::{
|
net::{
|
||||||
data_stream::{FrameGenerator, StreamTarget},
|
data_stream::{FrameGenerator, StreamFormat, StreamTarget},
|
||||||
miniconf::Miniconf,
|
miniconf::Miniconf,
|
||||||
serde::Deserialize,
|
serde::Deserialize,
|
||||||
telemetry::{Telemetry, TelemetryBuffer},
|
telemetry::{Telemetry, TelemetryBuffer},
|
||||||
|
@ -396,18 +396,43 @@ const APP: () = {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stream the data.
|
// Stream the data.
|
||||||
generator.add::<_, { SAMPLE_BUFFER_SIZE * 8 }>(0, |buf| {
|
generator.add::<_, { SAMPLE_BUFFER_SIZE * 8 }>(
|
||||||
let mut offset = 0;
|
StreamFormat::AdcDacData,
|
||||||
for device in [adc_samples.iter(), dac_samples.iter()] {
|
|buf| unsafe {
|
||||||
for channel in device {
|
let dst = buf.as_ptr() as usize as *mut u16;
|
||||||
for sample in channel.iter() {
|
|
||||||
buf[offset..offset + 2]
|
let adc0 = &adc_samples[0][0] as *const u16;
|
||||||
.copy_from_slice(&sample.to_ne_bytes());
|
core::ptr::copy_nonoverlapping(
|
||||||
offset += 2;
|
adc0,
|
||||||
}
|
dst,
|
||||||
}
|
SAMPLE_BUFFER_SIZE,
|
||||||
}
|
);
|
||||||
});
|
|
||||||
|
let dst = dst.add(SAMPLE_BUFFER_SIZE);
|
||||||
|
let adc1 = &adc_samples[1][0] as *const u16;
|
||||||
|
core::ptr::copy_nonoverlapping(
|
||||||
|
adc1,
|
||||||
|
dst,
|
||||||
|
SAMPLE_BUFFER_SIZE,
|
||||||
|
);
|
||||||
|
|
||||||
|
let dst = dst.add(SAMPLE_BUFFER_SIZE);
|
||||||
|
let dac0 = &dac_samples[0][0] as *const u16;
|
||||||
|
core::ptr::copy_nonoverlapping(
|
||||||
|
dac0,
|
||||||
|
dst,
|
||||||
|
SAMPLE_BUFFER_SIZE,
|
||||||
|
);
|
||||||
|
|
||||||
|
let dst = dst.add(SAMPLE_BUFFER_SIZE);
|
||||||
|
let dac1 = &dac_samples[1][0] as *const u16;
|
||||||
|
core::ptr::copy_nonoverlapping(
|
||||||
|
dac1,
|
||||||
|
dst,
|
||||||
|
SAMPLE_BUFFER_SIZE,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// Update telemetry measurements.
|
// Update telemetry measurements.
|
||||||
telemetry.adcs =
|
telemetry.adcs =
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
/// MQTT broker IPv4 address
|
/// MQTT broker IPv4 address
|
||||||
///
|
///
|
||||||
/// In the default configuration, the IP address is defined as 10.34.16.10.
|
/// In the default configuration, the IP address is defined as 10.34.16.10.
|
||||||
pub const MQTT_BROKER: [u8; 4] = [10, 34, 16, 10];
|
pub const MQTT_BROKER: [u8; 4] = [10, 35, 16, 10];
|
||||||
|
|
||||||
/// Sampling Frequency
|
/// Sampling Frequency
|
||||||
///
|
///
|
||||||
|
|
|
@ -15,6 +15,8 @@ use miniconf::MiniconfAtomic;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use smoltcp_nal::embedded_nal::{IpAddr, Ipv4Addr, SocketAddr, UdpClientStack};
|
use smoltcp_nal::embedded_nal::{IpAddr, Ipv4Addr, SocketAddr, UdpClientStack};
|
||||||
|
|
||||||
|
use crate::hardware::design_parameters::SAMPLE_BUFFER_SIZE;
|
||||||
|
|
||||||
use heapless::pool::{Box, Init, Pool, Uninit};
|
use heapless::pool::{Box, Init, Pool, Uninit};
|
||||||
|
|
||||||
use super::NetworkReference;
|
use super::NetworkReference;
|
||||||
|
@ -41,6 +43,15 @@ pub struct StreamTarget {
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Specifies the format of streamed data
|
||||||
|
#[repr(u16)]
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub enum StreamFormat {
|
||||||
|
/// Streamed data contains ADC0, ADC1, DAC0, and DAC1 sequentially in little-endian format. Each
|
||||||
|
/// batch is loaded into the stream frame sequentially until the frame is full.
|
||||||
|
AdcDacData = 0,
|
||||||
|
}
|
||||||
|
|
||||||
impl From<StreamTarget> for SocketAddr {
|
impl From<StreamTarget> for SocketAddr {
|
||||||
fn from(target: StreamTarget) -> SocketAddr {
|
fn from(target: StreamTarget) -> SocketAddr {
|
||||||
SocketAddr::new(
|
SocketAddr::new(
|
||||||
|
@ -86,23 +97,27 @@ pub fn setup_streaming(
|
||||||
}
|
}
|
||||||
|
|
||||||
struct StreamFrame {
|
struct StreamFrame {
|
||||||
format: u16,
|
format: StreamFormat,
|
||||||
sequence_number: u16,
|
sequence_number: u16,
|
||||||
buffer: Box<[u8; FRAME_SIZE], Init>,
|
buffer: Box<[u8; FRAME_SIZE], Init>,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
|
batch_count: u16,
|
||||||
|
batch_size: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StreamFrame {
|
impl StreamFrame {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
buffer: Box<[u8; FRAME_SIZE], Uninit>,
|
buffer: Box<[u8; FRAME_SIZE], Uninit>,
|
||||||
format: u16,
|
format: StreamFormat,
|
||||||
sequence_number: u16,
|
sequence_number: u16,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
format,
|
format,
|
||||||
offset: 4,
|
offset: 7,
|
||||||
sequence_number,
|
sequence_number,
|
||||||
buffer: unsafe { buffer.assume_init() },
|
buffer: unsafe { buffer.assume_init() },
|
||||||
|
batch_size: SAMPLE_BUFFER_SIZE as u8,
|
||||||
|
batch_count: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,6 +130,7 @@ impl StreamFrame {
|
||||||
let result = f(&mut self.buffer[self.offset..self.offset + T]);
|
let result = f(&mut self.buffer[self.offset..self.offset + T]);
|
||||||
|
|
||||||
self.offset += T;
|
self.offset += T;
|
||||||
|
self.batch_count = self.batch_count.checked_add(1).unwrap();
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -126,7 +142,9 @@ impl StreamFrame {
|
||||||
pub fn finish(&mut self) -> &[u8] {
|
pub fn finish(&mut self) -> &[u8] {
|
||||||
let offset = self.offset;
|
let offset = self.offset;
|
||||||
self.buffer[0..2].copy_from_slice(&self.sequence_number.to_ne_bytes());
|
self.buffer[0..2].copy_from_slice(&self.sequence_number.to_ne_bytes());
|
||||||
self.buffer[2..4].copy_from_slice(&self.format.to_ne_bytes());
|
self.buffer[2..4].copy_from_slice(&(self.format as u16).to_ne_bytes());
|
||||||
|
self.buffer[4..6].copy_from_slice(&self.batch_count.to_ne_bytes());
|
||||||
|
self.buffer[6] = self.batch_size;
|
||||||
&self.buffer[..offset]
|
&self.buffer[..offset]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,7 +170,7 @@ impl FrameGenerator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add<F, const T: usize>(&mut self, format: u16, f: F)
|
pub fn add<F, const T: usize>(&mut self, format: StreamFormat, f: F)
|
||||||
where
|
where
|
||||||
F: FnMut(&mut [u8]),
|
F: FnMut(&mut [u8]),
|
||||||
{
|
{
|
||||||
|
@ -171,6 +189,11 @@ impl FrameGenerator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
format == self.current_frame.as_ref().unwrap().format,
|
||||||
|
"Unexpected stream format encountered"
|
||||||
|
);
|
||||||
|
|
||||||
self.current_frame.as_mut().unwrap().add_batch::<_, T>(f);
|
self.current_frame.as_mut().unwrap().add_batch::<_, T>(f);
|
||||||
|
|
||||||
if self.current_frame.as_ref().unwrap().is_full::<T>() {
|
if self.current_frame.as_ref().unwrap().is_full::<T>() {
|
||||||
|
|
Loading…
Reference in New Issue