Adding signal generator control to dual-iir
This commit is contained in:
parent
a2f67ada3c
commit
9cca1497b7
@ -16,6 +16,7 @@ use stabilizer::{
|
|||||||
hal,
|
hal,
|
||||||
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},
|
||||||
@ -39,6 +40,13 @@ 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;
|
||||||
|
output_mode: [OutputMode; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct OutputMode {
|
||||||
|
IirFilter,
|
||||||
|
SignalGenerator,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Settings {
|
impl Default for Settings {
|
||||||
@ -59,6 +67,8 @@ impl Default for Settings {
|
|||||||
// The default telemetry period in seconds.
|
// The default telemetry period in seconds.
|
||||||
telemetry_period: 10,
|
telemetry_period: 10,
|
||||||
|
|
||||||
|
signal_generator: signal_generator::Config::default(),
|
||||||
|
|
||||||
stream_target: StreamTarget::default(),
|
stream_target: StreamTarget::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -172,24 +182,42 @@ const APP: () = {
|
|||||||
fence(Ordering::SeqCst);
|
fence(Ordering::SeqCst);
|
||||||
|
|
||||||
for channel in 0..adc_samples.len() {
|
for channel in 0..adc_samples.len() {
|
||||||
adc_samples[channel]
|
match settings.output_mode[channel] {
|
||||||
.iter()
|
OutputMode::IirFilter => {
|
||||||
.zip(dac_samples[channel].iter_mut())
|
adc_samples[channel]
|
||||||
.map(|(ai, di)| {
|
|
||||||
let x = f32::from(*ai as i16);
|
|
||||||
let y = settings.iir_ch[channel]
|
|
||||||
.iter()
|
.iter()
|
||||||
.zip(iir_state[channel].iter_mut())
|
.zip(dac_samples[channel].iter_mut())
|
||||||
.fold(x, |yi, (ch, state)| {
|
.map(|(ai, di)| {
|
||||||
ch.update(state, yi, hold)
|
let x = f32::from(*ai as i16);
|
||||||
});
|
let y = settings.iir_ch[channel]
|
||||||
// Note(unsafe): The filter limits must ensure that the value is in range.
|
.iter()
|
||||||
// The truncation introduces 1/2 LSB distortion.
|
.zip(iir_state[channel].iter_mut())
|
||||||
let y: i16 = unsafe { y.to_int_unchecked() };
|
.fold(x, |yi, (ch, state)| {
|
||||||
// Convert to DAC code
|
ch.update(state, yi, hold)
|
||||||
*di = DacCode::from(y).0;
|
});
|
||||||
})
|
// Note(unsafe): The filter limits must ensure that the value is in range.
|
||||||
.last();
|
// The truncation introduces 1/2 LSB distortion.
|
||||||
|
let y: i16 = unsafe { y.to_int_unchecked() };
|
||||||
|
// Convert to DAC code
|
||||||
|
*di = DacCode::from(y).0;
|
||||||
|
})
|
||||||
|
.last();
|
||||||
|
}
|
||||||
|
OutputMode::SignalGenerator => {
|
||||||
|
// 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]);
|
||||||
|
} else {
|
||||||
|
signal_generator.generate(&mut adc_samples[channel]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !settings.output_mode.iter().any(|&mode| mode == OutputMode::SignalGenerator) {
|
||||||
|
signal_generator.skip(adc_samples[0].len());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stream the data.
|
// Stream the data.
|
||||||
@ -230,6 +258,9 @@ const APP: () = {
|
|||||||
c.resources.afes.0.set_gain(settings.afe[0]);
|
c.resources.afes.0.set_gain(settings.afe[0]);
|
||||||
c.resources.afes.1.set_gain(settings.afe[1]);
|
c.resources.afes.1.set_gain(settings.afe[1]);
|
||||||
|
|
||||||
|
// Update the signal generator
|
||||||
|
c.resources.signal_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);
|
||||||
}
|
}
|
||||||
|
@ -66,13 +66,13 @@ struct InternalConf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Generator {
|
pub struct SignalGenerator {
|
||||||
index: u32,
|
index: u32,
|
||||||
config: InternalConf,
|
config: InternalConf,
|
||||||
pending_config: Option<InternalConf>,
|
pending_config: Option<InternalConf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Generator {
|
impl Default for SignalGenerator {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
config: Config::default().into(),
|
config: Config::default().into(),
|
||||||
@ -82,7 +82,7 @@ impl Default for Generator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Generator {
|
impl SignalGenerator {
|
||||||
/// Construct a new signal generator with some specific config.
|
/// Construct a new signal generator with some specific config.
|
||||||
///
|
///
|
||||||
/// # Args
|
/// # Args
|
||||||
@ -108,6 +108,18 @@ impl Generator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Skip `count` elements of the generator
|
||||||
|
pub fn skip(&mut self, count: usize) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.index = index % self.config.period;
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the next value in the generator sequence.
|
/// Get the next value in the generator sequence.
|
||||||
pub fn next(&mut self) -> i16 {
|
pub fn next(&mut self) -> i16 {
|
||||||
// When phase wraps, apply any new settings.
|
// When phase wraps, apply any new settings.
|
||||||
|
Loading…
Reference in New Issue
Block a user