Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/telemetry #341

Merged
merged 43 commits into from
May 10, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
330f67d
Adding WIP telemetry implementation for dual-iir
ryan-summers Apr 15, 2021
854f45a
Updating tick rates
ryan-summers Apr 15, 2021
f442418
Merge branch 'master' into feature/telemetry
ryan-summers Apr 15, 2021
403ff16
Resetting IP
ryan-summers Apr 15, 2021
1c9f30b
Merge branch 'rs/issue-276/unique-identifiers' into feature/telemetry
ryan-summers Apr 20, 2021
afcf058
Refactoring telemetry to support binaries
ryan-summers Apr 20, 2021
3ae319d
Updating readme
ryan-summers Apr 20, 2021
106236a
Merge branch 'master' into feature/telemetry
ryan-summers Apr 20, 2021
f49ba30
Merge branch 'feature/mqtt-rework' into feature/telemetry
ryan-summers Apr 29, 2021
9e355ca
Merge branch 'feature/mqtt-rework' into feature/telemetry
ryan-summers Apr 29, 2021
e746e2a
Adding WIP telemetry
ryan-summers Apr 29, 2021
4169cd8
Adding AFE functions
ryan-summers Apr 29, 2021
b35250e
Fixing ADC/voltage conversion functions
ryan-summers Apr 29, 2021
5767973
Merge branch 'feature/mqtt-rework' into feature/telemetry
ryan-summers May 4, 2021
4888f18
Adding rework to network module
ryan-summers May 4, 2021
06b328f
Adding WIP updates for telemetry
ryan-summers May 5, 2021
4a656ee
Adding refactor for telemetry
ryan-summers May 5, 2021
8144b3a
Updating constructors
ryan-summers May 5, 2021
0c69355
Adding updated telemetry implementation
ryan-summers May 5, 2021
8efd7d4
Fixing design parameters file
ryan-summers May 5, 2021
740e41d
Renaming files
ryan-summers May 5, 2021
e9c3e24
Merge branch 'master' into feature/telemetry
ryan-summers May 5, 2021
4cbc826
Adding documentation
ryan-summers May 6, 2021
949ea9c
Fixing build, formatting
ryan-summers May 6, 2021
81a292a
Fixing system timer
ryan-summers May 6, 2021
8c581ea
Merging lockin app functions
ryan-summers May 6, 2021
80b8716
Finalizing merge
ryan-summers May 6, 2021
03adb72
Fixing clippy
ryan-summers May 6, 2021
eeee5af
Updating dependencies
ryan-summers May 6, 2021
e07f0a4
Formatting
ryan-summers May 6, 2021
61321c4
Merge branch 'master' into feature/telemetry
ryan-summers May 6, 2021
ff79e0a
Fixing merge
ryan-summers May 6, 2021
89eaefd
Updating delay
ryan-summers May 6, 2021
7b76b1f
Updating after review
ryan-summers May 7, 2021
d68fa87
Simplifying settings lock
ryan-summers May 7, 2021
b73a4d9
Adding adc/dac code conversion utilities
ryan-summers May 7, 2021
923790b
Updating float conversion
ryan-summers May 7, 2021
60b1b11
Renaming AdcSample -> AdcCode
ryan-summers May 7, 2021
6e94ffc
Update src/hardware/dac.rs
ryan-summers May 7, 2021
fcda2d5
Addressing review feedback
ryan-summers May 10, 2021
65eb74b
Merge branch 'feature/telemetry' of github.com:quartiq/stabilizer int…
ryan-summers May 10, 2021
fa886d2
Cleaning up conversion + comments
ryan-summers May 10, 2021
81bc569
Simplifying unit conversions
ryan-summers May 10, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/bin/dual-iir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use serde::Deserialize;

use dsp::iir;
use hardware::{
Adc0Input, Adc1Input, AfeGain, Dac0Output, Dac1Output, DigitalInput0,
DigitalInput1, InputPin, SystemTimer, AFE0, AFE1,
Adc0Input, Adc1Input, AdcSample, AfeGain, Dac0Output, Dac1Output, DacCode,
DigitalInput0, DigitalInput1, InputPin, SystemTimer, AFE0, AFE1,
};

use net::{NetworkUsers, Telemetry, TelemetryBuffer, UpdateState};
Expand Down Expand Up @@ -154,15 +154,16 @@ const APP: () = {
// The truncation introduces 1/2 LSB distortion.
let y = unsafe { y.to_int_unchecked::<i16>() };
// Convert to DAC code
dac_samples[channel][sample] = y as u16 ^ 0x8000;
dac_samples[channel][sample] = DacCode::from(y).0;
}
}

// Update telemetry measurements.
c.resources.telemetry.adcs =
[adc_samples[0][0] as i16, adc_samples[1][0] as i16];
[AdcSample(adc_samples[0][0]), AdcSample(adc_samples[1][0])];

c.resources.telemetry.dacs = [dac_samples[0][0], dac_samples[1][0]];
c.resources.telemetry.dacs =
[DacCode(dac_samples[0][0]), DacCode(dac_samples[1][0])];

c.resources.telemetry.digital_inputs = digital_inputs;
}
Expand Down
13 changes: 7 additions & 6 deletions src/bin/lockin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ use dsp::{Accu, Complex, ComplexExt, Lockin, RPLL};
use stabilizer::net;

use stabilizer::hardware::{
design_parameters, setup, Adc0Input, Adc1Input, AfeGain, Dac0Output,
Dac1Output, DigitalInput0, DigitalInput1, InputStamper, SystemTimer, AFE0,
AFE1,
design_parameters, setup, Adc0Input, Adc1Input, AdcSample, AfeGain,
Dac0Output, Dac1Output, DacCode, DigitalInput0, DigitalInput1,
InputStamper, SystemTimer, AFE0, AFE1,
};

use miniconf::Miniconf;
Expand Down Expand Up @@ -234,15 +234,16 @@ const APP: () = {
Conf::Modulation => DAC_SEQUENCE[i] as i32,
};

*sample = value as u16 ^ 0x8000;
*sample = DacCode::from(value as i16).0;
}
}

// Update telemetry measurements.
c.resources.telemetry.adcs =
[adc_samples[0][0] as i16, adc_samples[1][0] as i16];
[AdcSample(adc_samples[0][0]), AdcSample(adc_samples[1][0])];

c.resources.telemetry.dacs = [dac_samples[0][0], dac_samples[1][0]];
c.resources.telemetry.dacs =
[DacCode(dac_samples[0][0]), DacCode(dac_samples[1][0])];
}

#[idle(resources=[network], spawn=[settings_update])]
Expand Down
22 changes: 22 additions & 0 deletions src/hardware/adc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,28 @@ use hal::dma::{
MemoryToPeripheral, PeripheralToMemory, Transfer,
};

/// A type representing an ADC sample.
#[derive(Copy, Clone)]
pub struct AdcSample(pub u16);
ryan-summers marked this conversation as resolved.
Show resolved Hide resolved

impl Into<f32> for AdcSample {
/// Convert raw ADC codes to/from voltage levels.
///
/// # Note
///
/// This does not account for the programmable gain amplifier at the signal input.
fn into(self) -> f32 {
// The input voltage is measured by the ADC across a dynamic scale of +/- 4.096 V with a
// dynamic range across signed integers. Additionally, the signal is fully differential, so
// the differential voltage is measured at the ADC. Thus, the single-ended signal is
// measured at the input is half of the ADC-reported measurement. As a pre-filter, the
// input signal has a fixed gain of 1/5 through a static input active filter.
ryan-summers marked this conversation as resolved.
Show resolved Hide resolved
let adc_volts_per_lsb = 5.0 / 2.0 * 4.096 / i16::MAX as f32;
ryan-summers marked this conversation as resolved.
Show resolved Hide resolved

(self.0 as i16) as f32 * adc_volts_per_lsb
}
}

// The following data is written by the timer ADC sample trigger into the SPI CR1 to start the
// transfer. Data in AXI SRAM is not initialized on boot, so the contents are random. This value is
// initialized during setup.
Expand Down
33 changes: 33 additions & 0 deletions src/hardware/dac.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,39 @@ use hal::dma::{
static mut DAC_BUF: [[[u16; SAMPLE_BUFFER_SIZE]; 3]; 2] =
[[[0; SAMPLE_BUFFER_SIZE]; 3]; 2];

/// Custom type for referencing DAC output codes.
/// The internal integer is the raw code written to the DAC output register.
#[derive(Copy, Clone)]
pub struct DacCode(pub u16);

impl Into<f32> for DacCode {
fn into(self) -> f32 {
// The output voltage is generated by the DAC with an output range of +/- 4.096 V. This
// signal then passes through a 2.5x gain stage. Note that the DAC operates using unsigned
// integers, and u16::MAX / 2 is considered zero voltage output. Thus, the dynamic range of
// the output stage is +/- 10.24 V. At a DAC code of zero, there is an output of -10.24 V,
// and at a max DAC code, there is an output of 10.24 V.
//
// Note: The MAX code corresponding to +VREF is not programmable, as it is 1 bit larger
// than full-scale.
let dac_volts_per_lsb = 10.24 * 2.0 / (u16::MAX as u32 + 1) as f32;

(self.0 as f32) * dac_volts_per_lsb - 10.24
ryan-summers marked this conversation as resolved.
Show resolved Hide resolved
}
}

impl From<i16> for DacCode {
/// Generate a DAC code from a 16-bit signed value.
///
/// # Note
/// This is provided as a means to convert between the DACs internal 16-bit, unsigned register
/// and a 2s-complement integer that is convenient when using the DAC in the bipolar
/// configuration.
fn from(value: i16) -> Self {
Self(value as u16 ^ 0x8000)
}
ryan-summers marked this conversation as resolved.
Show resolved Hide resolved
}

macro_rules! dac_output {
($name:ident, $index:literal, $data_stream:ident,
$spi:ident, $trigger_channel:ident, $dma_req:ident) => {
Expand Down
4 changes: 2 additions & 2 deletions src/hardware/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ pub mod pounder;
mod system_timer;
mod timers;

pub use adc::{Adc0Input, Adc1Input};
pub use adc::{Adc0Input, Adc1Input, AdcSample};
pub use afe::Gain as AfeGain;
pub use cycle_counter::CycleCounter;
pub use dac::{Dac0Output, Dac1Output};
pub use dac::{Dac0Output, Dac1Output, DacCode};
pub use digital_input_stamper::InputStamper;
pub use pounder::DdsOutput;
pub use system_timer::SystemTimer;
Expand Down
42 changes: 10 additions & 32 deletions src/net/telemetry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ use minimq::QoS;
use serde::Serialize;

use super::NetworkReference;
use crate::hardware::{design_parameters::MQTT_BROKER, AfeGain};
use crate::hardware::{
design_parameters::MQTT_BROKER, AdcSample, AfeGain, DacCode,
};

/// The telemetry client for reporting telemetry data over MQTT.
pub struct TelemetryClient<T: Serialize> {
Expand All @@ -33,9 +35,9 @@ pub struct TelemetryClient<T: Serialize> {
#[derive(Copy, Clone)]
pub struct TelemetryBuffer {
/// The latest input sample on ADC0/ADC1.
pub adcs: [i16; 2],
pub adcs: [AdcSample; 2],
/// The latest output code on DAC0/DAC1.
pub dacs: [u16; 2],
pub dacs: [DacCode; 2],
/// The latest digital input states during processing.
pub digital_inputs: [bool; 2],
}
Expand All @@ -55,8 +57,8 @@ pub struct Telemetry {
impl Default for TelemetryBuffer {
fn default() -> Self {
Self {
adcs: [0, 0],
dacs: [0, 0],
adcs: [AdcSample(0), AdcSample(0)],
dacs: [DacCode(0), DacCode(0)],
digital_inputs: [false, false],
}
}
Expand All @@ -72,36 +74,12 @@ impl TelemetryBuffer {
/// # Returns
/// The finalized telemetry structure that can be serialized and reported.
pub fn finalize(self, afe0: AfeGain, afe1: AfeGain) -> Telemetry {
// The input voltage is measured by the ADC across a dynamic scale of +/- 4.096 V with a
// dynamic range across signed integers. Additionally, the signal is fully differential, so
// the differential voltage is measured at the ADC. Thus, the single-ended signal is
// measured at the input is half of the ADC-reported measurement. As a pre-filter, the
// input signal has a fixed gain of 1/5 through a static input active filter. Finally, at
// the very front-end of the signal, there's an analog input multiplier that is
// configurable by the user.
let adc_volts_per_lsb = 5.0 * 4.096 / 2.0 / i16::MAX as f32;
let in0_volts =
(adc_volts_per_lsb * self.adcs[0] as f32) / afe0.as_multiplier();
let in1_volts =
(adc_volts_per_lsb * self.adcs[1] as f32) / afe1.as_multiplier();

// The output voltage is generated by the DAC with an output range of +/- 4.096 V. This
// signal then passes through a 2.5x gain stage. Note that the DAC operates using unsigned
// integers, and u16::MAX / 2 is considered zero voltage output. Thus, the dynamic range of
// the output stage is +/- 10.24 V. At a DAC code of zero, there is an output of -10.24 V,
// and at a max DAC code, there is an output of 10.24 V.
//
// Note: The MAX code corresponding to +VREF is not programmable, as it is 1 bit larger
// than full-scale.
let dac_volts_per_lsb = 10.24 * 2.0 / (u16::MAX as u32 + 1) as f32;
let dac_offset = -10.24;

let out0_volts = dac_volts_per_lsb * self.dacs[0] as f32 + dac_offset;
let out1_volts = dac_volts_per_lsb * self.dacs[1] as f32 + dac_offset;
let in0_volts = Into::<f32>::into(self.adcs[0]) / afe0.as_multiplier();
let in1_volts = Into::<f32>::into(self.adcs[1]) / afe1.as_multiplier();

Telemetry {
adcs: [in0_volts, in1_volts],
dacs: [out0_volts, out1_volts],
dacs: [self.dacs[0].into(), self.dacs[1].into()],
digital_inputs: self.digital_inputs,
}
}
Expand Down