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},
|
dac::{Dac0Output, Dac1Output, DacCode},
|
||||||
embedded_hal::digital::v2::InputPin,
|
embedded_hal::digital::v2::InputPin,
|
||||||
hal,
|
hal,
|
||||||
|
signal_generator::{self, SignalGenerator},
|
||||||
system_timer::SystemTimer,
|
system_timer::SystemTimer,
|
||||||
DigitalInput0, DigitalInput1, AFE0, AFE1,
|
DigitalInput0, DigitalInput1, AFE0, AFE1,
|
||||||
signal_generator::SignalGenerator,
|
|
||||||
},
|
},
|
||||||
net::{
|
net::{
|
||||||
data_stream::{BlockGenerator, StreamTarget},
|
data_stream::{BlockGenerator, StreamTarget},
|
||||||
|
@ -40,11 +40,12 @@ pub struct Settings {
|
||||||
force_hold: bool,
|
force_hold: bool,
|
||||||
telemetry_period: u16,
|
telemetry_period: u16,
|
||||||
stream_target: StreamTarget,
|
stream_target: StreamTarget,
|
||||||
signal_generator: signal_generator::Config;
|
signal_generator: signal_generator::Config,
|
||||||
output_mode: [OutputMode; 2],
|
output_mode: [OutputMode; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct OutputMode {
|
#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Miniconf)]
|
||||||
|
pub enum OutputMode {
|
||||||
IirFilter,
|
IirFilter,
|
||||||
SignalGenerator,
|
SignalGenerator,
|
||||||
}
|
}
|
||||||
|
@ -70,6 +71,7 @@ impl Default for Settings {
|
||||||
signal_generator: signal_generator::Config::default(),
|
signal_generator: signal_generator::Config::default(),
|
||||||
|
|
||||||
stream_target: StreamTarget::default(),
|
stream_target: StreamTarget::default(),
|
||||||
|
output_mode: [OutputMode::IirFilter, OutputMode::IirFilter],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,6 +85,7 @@ const APP: () = {
|
||||||
dacs: (Dac0Output, Dac1Output),
|
dacs: (Dac0Output, Dac1Output),
|
||||||
network: NetworkUsers<Settings, Telemetry>,
|
network: NetworkUsers<Settings, Telemetry>,
|
||||||
generator: BlockGenerator,
|
generator: BlockGenerator,
|
||||||
|
signal_generator: SignalGenerator,
|
||||||
|
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
telemetry: TelemetryBuffer,
|
telemetry: TelemetryBuffer,
|
||||||
|
@ -123,6 +126,8 @@ const APP: () = {
|
||||||
// Start sampling ADCs.
|
// Start sampling ADCs.
|
||||||
stabilizer.adc_dac_timer.start();
|
stabilizer.adc_dac_timer.start();
|
||||||
|
|
||||||
|
let settings = Settings::default();
|
||||||
|
|
||||||
init::LateResources {
|
init::LateResources {
|
||||||
afes: stabilizer.afes,
|
afes: stabilizer.afes,
|
||||||
adcs: stabilizer.adcs,
|
adcs: stabilizer.adcs,
|
||||||
|
@ -131,7 +136,8 @@ const APP: () = {
|
||||||
network,
|
network,
|
||||||
digital_inputs: stabilizer.digital_inputs,
|
digital_inputs: stabilizer.digital_inputs,
|
||||||
telemetry: TelemetryBuffer::default(),
|
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
|
/// 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.
|
/// 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)]
|
#[inline(never)]
|
||||||
#[link_section = ".itcm.process"]
|
#[link_section = ".itcm.process"]
|
||||||
fn process(mut c: process::Context) {
|
fn process(mut c: process::Context) {
|
||||||
|
@ -163,6 +169,7 @@ const APP: () = {
|
||||||
ref mut iir_state,
|
ref mut iir_state,
|
||||||
ref mut telemetry,
|
ref mut telemetry,
|
||||||
ref mut generator,
|
ref mut generator,
|
||||||
|
ref mut signal_generator,
|
||||||
} = c.resources;
|
} = c.resources;
|
||||||
|
|
||||||
let digital_inputs = [
|
let digital_inputs = [
|
||||||
|
@ -207,17 +214,25 @@ const APP: () = {
|
||||||
// Do not generate the samples twice, or we may mess up phasing of the
|
// Do not generate the samples twice, or we may mess up phasing of the
|
||||||
// signal generator. Instead, copy the previously-generated signal.
|
// signal generator. Instead, copy the previously-generated signal.
|
||||||
// TODO: Is there a nicer way we can handle this edge case?
|
// TODO: Is there a nicer way we can handle this edge case?
|
||||||
if (channel == 1) && settings.output_mode[0] == OutputMode::SignalGenerator {
|
if (channel == 1)
|
||||||
adc_samples[1].copy_from_slice(adc_samples[0]);
|
&& settings.output_mode[0]
|
||||||
|
== OutputMode::SignalGenerator
|
||||||
|
{
|
||||||
|
*dac_samples[1] = *dac_samples[0];
|
||||||
} else {
|
} 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) {
|
if !settings
|
||||||
signal_generator.skip(adc_samples[0].len());
|
.output_mode
|
||||||
|
.iter()
|
||||||
|
.any(|&mode| mode == OutputMode::SignalGenerator)
|
||||||
|
{
|
||||||
|
signal_generator.skip(adc_samples[0].len() as u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stream the data.
|
// 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) {
|
fn settings_update(mut c: settings_update::Context) {
|
||||||
// Update the IIR channels.
|
// Update the IIR channels.
|
||||||
let settings = c.resources.network.miniconf.settings();
|
let settings = c.resources.network.miniconf.settings();
|
||||||
|
@ -259,7 +274,9 @@ const APP: () = {
|
||||||
c.resources.afes.1.set_gain(settings.afe[1]);
|
c.resources.afes.1.set_gain(settings.afe[1]);
|
||||||
|
|
||||||
// Update the signal generator
|
// 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();
|
let target = settings.stream_target.into();
|
||||||
c.resources.network.direct_stream(target);
|
c.resources.network.direct_stream(target);
|
||||||
|
|
|
@ -96,7 +96,7 @@ const APP: () = {
|
||||||
telemetry: TelemetryBuffer,
|
telemetry: TelemetryBuffer,
|
||||||
digital_inputs: (DigitalInput0, DigitalInput1),
|
digital_inputs: (DigitalInput0, DigitalInput1),
|
||||||
generator: BlockGenerator,
|
generator: BlockGenerator,
|
||||||
signal_generator: signal_generator::Generator,
|
signal_generator: signal_generator::SignalGenerator,
|
||||||
|
|
||||||
timestamper: InputStamper,
|
timestamper: InputStamper,
|
||||||
pll: RPLL,
|
pll: RPLL,
|
||||||
|
@ -156,7 +156,7 @@ const APP: () = {
|
||||||
digital_inputs: stabilizer.digital_inputs,
|
digital_inputs: stabilizer.digital_inputs,
|
||||||
timestamper: stabilizer.timestamper,
|
timestamper: stabilizer.timestamper,
|
||||||
telemetry: TelemetryBuffer::default(),
|
telemetry: TelemetryBuffer::default(),
|
||||||
signal_generator: signal_generator::Generator::new(
|
signal_generator: signal_generator::SignalGenerator::new(
|
||||||
signal_generator::Config {
|
signal_generator::Config {
|
||||||
period: design_parameters::SAMPLE_BUFFER_SIZE as u32,
|
period: design_parameters::SAMPLE_BUFFER_SIZE as u32,
|
||||||
symmetry: 0.5,
|
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 {
|
pub enum Signal {
|
||||||
Sine,
|
Sine,
|
||||||
Square,
|
Square,
|
||||||
Triangle,
|
Triangle,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug, Miniconf, Deserialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
// TODO: Should period be specified in Hz?
|
// TODO: Should period be specified in Hz?
|
||||||
pub period: u32,
|
pub period: u32,
|
||||||
|
@ -102,19 +106,21 @@ impl SignalGenerator {
|
||||||
///
|
///
|
||||||
/// # Args
|
/// # Args
|
||||||
/// * `samples` - The location to store generated values into.
|
/// * `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() {
|
for sample in samples.iter_mut() {
|
||||||
*sample = self.next();
|
*sample = DacCode::from(self.next()).0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Skip `count` elements of the generator
|
/// 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);
|
let index = self.index.wrapping_add(count);
|
||||||
|
|
||||||
// If we skip past the period of the signal, apply any pending config.
|
// 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 {
|
||||||
self.config = config;
|
if let Some(config) = self.pending_config.take() {
|
||||||
|
self.config = config;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.index = index % self.config.period;
|
self.index = index % self.config.period;
|
||||||
|
@ -140,25 +146,37 @@ impl SignalGenerator {
|
||||||
}
|
}
|
||||||
Signal::Triangle => {
|
Signal::Triangle => {
|
||||||
if phase < self.config.phase_symmetry {
|
if phase < self.config.phase_symmetry {
|
||||||
let duration_of_phase = (self.config.phase_symmetry.wrapping_sub(i32::MIN) >> 16) as u16;
|
let duration_of_phase =
|
||||||
let phase_progress = (phase.wrapping_sub(i32::MIN) >> 16) as u16;
|
(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 {
|
if duration_of_phase == 0 {
|
||||||
i16::MIN
|
i16::MIN
|
||||||
} else {
|
} else {
|
||||||
i16::MIN.wrapping_add((u16::MAX as u32 * phase_progress as u32 /
|
i16::MIN.wrapping_add(
|
||||||
duration_of_phase as u32) as i16)
|
(u16::MAX as u32 * phase_progress as u32
|
||||||
|
/ duration_of_phase as u32)
|
||||||
|
as i16,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
let duration_of_phase =
|
||||||
let duration_of_phase = (i32::MAX.wrapping_sub(self.config.phase_symmetry) >> 16) as u16;
|
(i32::MAX.wrapping_sub(self.config.phase_symmetry)
|
||||||
let phase_progress = (phase.wrapping_sub(self.config.phase_symmetry) >> 16) as
|
>> 16) as u16;
|
||||||
u16;
|
let phase_progress = (phase
|
||||||
|
.wrapping_sub(self.config.phase_symmetry)
|
||||||
|
>> 16) as u16;
|
||||||
|
|
||||||
if duration_of_phase == 0 {
|
if duration_of_phase == 0 {
|
||||||
i16::MAX
|
i16::MAX
|
||||||
} else {
|
} 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