Adding stream format, updating header format

master
Ryan Summers 2021-07-23 15:08:07 +02:00
parent 6c87db3778
commit 8a143a3f58
5 changed files with 119 additions and 57 deletions

View File

@ -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

View File

@ -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 }>(
let dst = buf.as_ptr() as usize as *mut u16; StreamFormat::AdcDacData,
|buf| unsafe {
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 =

View File

@ -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 =

View File

@ -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
/// ///

View File

@ -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>() {