Adding initial working proof of concept
This commit is contained in:
parent
9cca1497b7
commit
68859c387a
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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,19 +106,21 @@ 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() {
|
||||
self.config = config;
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user