Adding initial working proof of concept

This commit is contained in:
Ryan Summers 2021-06-28 13:40:59 +02:00
parent 9cca1497b7
commit 68859c387a
3 changed files with 65 additions and 30 deletions

View File

@ -14,9 +14,9 @@ use stabilizer::{
dac::{Dac0Output, Dac1Output, DacCode},
embedded_hal::digital::v2::InputPin,
hal,
signal_generator::{self, SignalGenerator},
system_timer::SystemTimer,
DigitalInput0, DigitalInput1, AFE0, AFE1,
signal_generator::SignalGenerator,
},
net::{
data_stream::{BlockGenerator, StreamTarget},
@ -40,11 +40,12 @@ pub struct Settings {
force_hold: bool,
telemetry_period: u16,
stream_target: StreamTarget,
signal_generator: signal_generator::Config;
signal_generator: signal_generator::Config,
output_mode: [OutputMode; 2],
}
pub struct OutputMode {
#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Miniconf)]
pub enum OutputMode {
IirFilter,
SignalGenerator,
}
@ -70,6 +71,7 @@ impl Default for Settings {
signal_generator: signal_generator::Config::default(),
stream_target: StreamTarget::default(),
output_mode: [OutputMode::IirFilter, OutputMode::IirFilter],
}
}
}
@ -83,6 +85,7 @@ const APP: () = {
dacs: (Dac0Output, Dac1Output),
network: NetworkUsers<Settings, Telemetry>,
generator: BlockGenerator,
signal_generator: SignalGenerator,
settings: Settings,
telemetry: TelemetryBuffer,
@ -123,6 +126,8 @@ const APP: () = {
// Start sampling ADCs.
stabilizer.adc_dac_timer.start();
let settings = Settings::default();
init::LateResources {
afes: stabilizer.afes,
adcs: stabilizer.adcs,
@ -131,7 +136,8 @@ const APP: () = {
network,
digital_inputs: stabilizer.digital_inputs,
telemetry: TelemetryBuffer::default(),
settings: Settings::default(),
settings,
signal_generator: SignalGenerator::new(settings.signal_generator),
}
}
@ -151,7 +157,7 @@ const APP: () = {
///
/// Because the ADC and DAC operate at the same rate, these two constraints actually implement
/// the same time bounds, meeting one also means the other is also met.
#[task(binds=DMA1_STR4, resources=[adcs, digital_inputs, dacs, iir_state, settings, telemetry, generator], priority=2)]
#[task(binds=DMA1_STR4, resources=[adcs, digital_inputs, dacs, iir_state, settings, signal_generator, telemetry, generator], priority=2)]
#[inline(never)]
#[link_section = ".itcm.process"]
fn process(mut c: process::Context) {
@ -163,6 +169,7 @@ const APP: () = {
ref mut iir_state,
ref mut telemetry,
ref mut generator,
ref mut signal_generator,
} = c.resources;
let digital_inputs = [
@ -207,17 +214,25 @@ const APP: () = {
// Do not generate the samples twice, or we may mess up phasing of the
// signal generator. Instead, copy the previously-generated signal.
// TODO: Is there a nicer way we can handle this edge case?
if (channel == 1) && settings.output_mode[0] == OutputMode::SignalGenerator {
adc_samples[1].copy_from_slice(adc_samples[0]);
if (channel == 1)
&& settings.output_mode[0]
== OutputMode::SignalGenerator
{
*dac_samples[1] = *dac_samples[0];
} else {
signal_generator.generate(&mut adc_samples[channel]);
signal_generator
.generate(&mut dac_samples[channel][..]);
}
}
}
}
if !settings.output_mode.iter().any(|&mode| mode == OutputMode::SignalGenerator) {
signal_generator.skip(adc_samples[0].len());
if !settings
.output_mode
.iter()
.any(|&mode| mode == OutputMode::SignalGenerator)
{
signal_generator.skip(adc_samples[0].len() as u32);
}
// Stream the data.
@ -248,7 +263,7 @@ const APP: () = {
}
}
#[task(priority = 1, resources=[network, afes, settings])]
#[task(priority = 1, resources=[network, afes, settings, signal_generator])]
fn settings_update(mut c: settings_update::Context) {
// Update the IIR channels.
let settings = c.resources.network.miniconf.settings();
@ -259,7 +274,9 @@ const APP: () = {
c.resources.afes.1.set_gain(settings.afe[1]);
// Update the signal generator
c.resources.signal_generator.update_waveform(settings.signal_generator);
c.resources.signal_generator.lock(|generator| {
generator.update_waveform(settings.signal_generator)
});
let target = settings.stream_target.into();
c.resources.network.direct_stream(target);

View File

@ -96,7 +96,7 @@ const APP: () = {
telemetry: TelemetryBuffer,
digital_inputs: (DigitalInput0, DigitalInput1),
generator: BlockGenerator,
signal_generator: signal_generator::Generator,
signal_generator: signal_generator::SignalGenerator,
timestamper: InputStamper,
pll: RPLL,
@ -156,7 +156,7 @@ const APP: () = {
digital_inputs: stabilizer.digital_inputs,
timestamper: stabilizer.timestamper,
telemetry: TelemetryBuffer::default(),
signal_generator: signal_generator::Generator::new(
signal_generator: signal_generator::SignalGenerator::new(
signal_generator::Config {
period: design_parameters::SAMPLE_BUFFER_SIZE as u32,
symmetry: 0.5,

View File

@ -1,11 +1,15 @@
#[derive(Copy, Clone, Debug)]
use crate::hardware::dac::DacCode;
use miniconf::Miniconf;
use serde::Deserialize;
#[derive(Copy, Clone, Debug, Deserialize, Miniconf)]
pub enum Signal {
Sine,
Square,
Triangle,
}
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug, Miniconf, Deserialize)]
pub struct Config {
// TODO: Should period be specified in Hz?
pub period: u32,
@ -102,20 +106,22 @@ impl SignalGenerator {
///
/// # Args
/// * `samples` - The location to store generated values into.
pub fn generate(&mut self, samples: &mut [i16]) {
pub fn generate(&mut self, samples: &mut [u16]) {
for sample in samples.iter_mut() {
*sample = self.next();
*sample = DacCode::from(self.next()).0;
}
}
/// Skip `count` elements of the generator
pub fn skip(&mut self, count: usize) {
pub fn skip(&mut self, count: u32) {
let index = self.index.wrapping_add(count);
// If we skip past the period of the signal, apply any pending config.
if index > self.config.period && let Some(config) = self.pendig_config.take() {
if index > self.config.period {
if let Some(config) = self.pending_config.take() {
self.config = config;
}
}
self.index = index % self.config.period;
}
@ -140,25 +146,37 @@ impl SignalGenerator {
}
Signal::Triangle => {
if phase < self.config.phase_symmetry {
let duration_of_phase = (self.config.phase_symmetry.wrapping_sub(i32::MIN) >> 16) as u16;
let phase_progress = (phase.wrapping_sub(i32::MIN) >> 16) as u16;
let duration_of_phase =
(self.config.phase_symmetry.wrapping_sub(i32::MIN)
>> 16) as u16;
let phase_progress =
(phase.wrapping_sub(i32::MIN) >> 16) as u16;
if duration_of_phase == 0 {
i16::MIN
} else {
i16::MIN.wrapping_add((u16::MAX as u32 * phase_progress as u32 /
duration_of_phase as u32) as i16)
i16::MIN.wrapping_add(
(u16::MAX as u32 * phase_progress as u32
/ duration_of_phase as u32)
as i16,
)
}
} else {
let duration_of_phase = (i32::MAX.wrapping_sub(self.config.phase_symmetry) >> 16) as u16;
let phase_progress = (phase.wrapping_sub(self.config.phase_symmetry) >> 16) as
u16;
let duration_of_phase =
(i32::MAX.wrapping_sub(self.config.phase_symmetry)
>> 16) as u16;
let phase_progress = (phase
.wrapping_sub(self.config.phase_symmetry)
>> 16) as u16;
if duration_of_phase == 0 {
i16::MAX
} else {
i16::MAX.wrapping_sub((u16::MAX as u32 * phase_progress as u32 / duration_of_phase as u32) as i16)
i16::MAX.wrapping_sub(
(u16::MAX as u32 * phase_progress as u32
/ duration_of_phase as u32)
as i16,
)
}
}
}