From c4979f08f6c7794a8e300b1e53f7b719f6d7e51d Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 11 Jun 2020 11:51:52 +0200 Subject: [PATCH] Updating code comments --- ad9959/src/lib.rs | 56 ++++++++++++++++ stabilizer/src/afe.rs | 8 +++ stabilizer/src/pounder/attenuators.rs | 21 ++++++ stabilizer/src/pounder/mod.rs | 95 ++++++++++++++++++++++++++- stabilizer/src/pounder/rf_power.rs | 7 ++ stabilizer/src/server.rs | 34 ++++++++++ 6 files changed, 219 insertions(+), 2 deletions(-) diff --git a/ad9959/src/lib.rs b/ad9959/src/lib.rs index 75277e6..14f8ddd 100644 --- a/ad9959/src/lib.rs +++ b/ad9959/src/lib.rs @@ -24,6 +24,7 @@ pub struct Ad9959 { io_update: UPDATE, } +/// A trait that allows a HAL to provide a means of communicating with the AD9959. pub trait Interface { type Error; @@ -34,6 +35,8 @@ pub trait Interface { fn read(&mut self, addr: u8, dest: &mut [u8]) -> Result<(), Self::Error>; } +/// Indicates various communication modes of the DDS. The value of this enumeration is equivalent to +/// the configuration bits of the DDS CSR register. #[derive(Copy, Clone, PartialEq)] pub enum Mode { SingleBitTwoWire = 0b00, @@ -103,6 +106,17 @@ where UPDATE: OutputPin, { + /// Construct and initialize the DDS. + /// + /// Args: + /// * `interface` - An interface to the DDS. + /// * `reset_pin` - A pin connected to the DDS reset input. + /// * `io_update` - A pin connected to the DDS io_update input. + /// * `delay` - A delay implementation for blocking operation for specific amounts of time. + /// * `desired_mode` - The desired communication mode of the interface to the DDS. + /// * `clock_frequency` - The clock frequency of the reference clock input. + /// * `multiplier` - The desired clock multiplier for the system clock. This multiplies + /// `clock_frequency` to generate the system clock. pub fn new(interface: INTERFACE, reset_pin: &mut RST, io_update: UPDATE, @@ -158,6 +172,7 @@ where Ok(ad9959) } + /// Latch the DDS configuration to ensure it is active on the output channels. fn latch_configuration(&mut self) -> Result<(), Error> { self.io_update.set_high().or_else(|_| Err(Error::Pin))?; // The SYNC_CLK is 1/4 the system clock frequency. The IO_UPDATE pin must be latched for one @@ -205,10 +220,12 @@ where Ok(self.system_clock_frequency()) } + /// Get the current reference clock frequency in Hz. pub fn get_reference_clock_frequency(&self) -> f32 { self.reference_clock_frequency } + /// Get the current reference clock multiplier. pub fn get_reference_clock_multiplier(&mut self) -> Result> { let mut fr1: [u8; 3] = [0, 0, 0]; self.interface.read(Register::FR1 as u8, &mut fr1)?; @@ -257,6 +274,7 @@ where Ok(true) } + /// Get the current system clock frequency in Hz. fn system_clock_frequency(&self) -> f64 { self.system_clock_multiplier as f64 * self.reference_clock_frequency as f64 } @@ -281,6 +299,7 @@ where Ok(()) } + /// Determine if an output channel is enabled. pub fn is_enabled(&mut self, channel: Channel) -> Result> { let mut csr: [u8; 1] = [0; 1]; self.interface.read(Register::CSR as u8, &mut csr)?; @@ -288,7 +307,15 @@ where Ok(csr[0].get_bit(channel as usize + 4)) } + /// Update an output channel configuration register. + /// + /// Args: + /// * `channel` - The channel to configure. + /// * `register` - The register to update. + /// * `data` - The contents to write to the provided register. fn modify_channel(&mut self, channel: Channel, register: Register, data: &[u8]) -> Result<(), Error> { + // Disable all other outputs so that we can update the configuration register of only the + // specified channel. let mut csr: [u8; 1] = [0]; self.interface.read(Register::CSR as u8, &mut csr)?; @@ -308,7 +335,15 @@ where Ok(()) } + /// Read a configuration register of a specific channel. + /// + /// Args: + /// * `channel` - The channel to read. + /// * `register` - The register to read. + /// * `data` - A location to store the read register contents. fn read_channel(&mut self, channel: Channel, register: Register, mut data: &mut [u8]) -> Result<(), Error> { + // Disable all other channels in the CSR so that we can read the configuration register of + // only the desired channel. let mut csr: [u8; 1] = [0]; self.interface.read(Register::CSR as u8, &mut csr)?; @@ -343,6 +378,13 @@ where Ok((phase_offset as f32) / ((1 << 14) as f32)) } + /// Get the current phase of a specified channel. + /// + /// Args: + /// * `channel` - The channel to get the phase of. + /// + /// Returns: + /// The phase of the channel in turns. pub fn get_phase(&mut self, channel: Channel) -> Result> { let mut phase_offset: [u8; 2] = [0; 2]; self.read_channel(channel, Register::CPOW0, &mut phase_offset)?; @@ -387,6 +429,13 @@ where Ok(amplitude_control as f32 / (1 << 10) as f32) } + /// Get the configured amplitude of a channel. + /// + /// Args: + /// * `channel` - The channel to get the amplitude of. + /// + /// Returns: + /// The normalized amplitude of the channel. pub fn get_amplitude(&mut self, channel: Channel) -> Result> { let mut acr: [u8; 3] = [0; 3]; self.read_channel(channel, Register::ACR, &mut acr)?; @@ -421,6 +470,13 @@ where Ok((tuning_word as f64 / 1u64.wrapping_shl(32) as f64) * self.system_clock_frequency()) } + /// Get the frequency of a channel. + /// + /// Arguments: + /// * `channel` - The channel to get the frequency of. + /// + /// Returns: + /// The frequency of the channel in Hz. pub fn get_frequency(&mut self, channel: Channel) -> Result> { // Read the frequency tuning word for the channel. let mut tuning_word: [u8; 4] = [0; 4]; diff --git a/stabilizer/src/afe.rs b/stabilizer/src/afe.rs index 868c475..62d9cb4 100644 --- a/stabilizer/src/afe.rs +++ b/stabilizer/src/afe.rs @@ -12,6 +12,7 @@ pub enum Gain { G10 = 0b11 } +/// A programmable gain amplifier that allows for setting the gain via GPIO. pub struct ProgrammableGainAmplifier { a0: A0, a1: A1 @@ -38,6 +39,11 @@ where A1: embedded_hal::digital::v2::StatefulOutputPin, A1::Error: core::fmt::Debug, { + /// Construct a new programmable gain driver. + /// + /// Args: + /// * `a0` - An output connected to the A0 input of the amplifier. + /// * `a1` - An output connected to the A1 input of the amplifier. pub fn new(a0: A0, a1: A1) -> Self { let mut afe = Self { a0: a0, a1: a1}; @@ -47,6 +53,7 @@ where afe } + /// Set the gain of the front-end. pub fn set_gain(&mut self, gain: Gain) { if (gain as u8 & 0b01) != 0 { self.a0.set_high().unwrap(); @@ -61,6 +68,7 @@ where } } + /// Get the programmed gain of the analog front-end. pub fn get_gain(&self) -> Result { let mut code: u8 = 0; if self.a0.is_set_high().unwrap() { diff --git a/stabilizer/src/pounder/attenuators.rs b/stabilizer/src/pounder/attenuators.rs index 5e1dc8f..2042f47 100644 --- a/stabilizer/src/pounder/attenuators.rs +++ b/stabilizer/src/pounder/attenuators.rs @@ -1,6 +1,20 @@ use super::{Channel, Error}; +/// Provide an interface for managing digital attenuators on Pounder hardware. +/// +/// Note: The digital attenuators do not allow read-back of attenuation. To circumvent this, this +/// driver maintains the attenuation code in both the shift register as well as the latched output +/// register of the attenuators. This allows the "active" attenuation code to be read back by +/// reading the shfit register. The downside of this approach is that any read is destructive, so a +/// read-writeback approach is employed. pub trait AttenuatorInterface { + + /// Set the attenuation of a single channel. + /// + /// Args: + /// * `channel` - The pounder channel to configure the attenuation of. + /// * `attenuation` - The desired attenuation of the channel in dB. This has a resolution of + /// 0.5dB. fn set_attenuation(&mut self, channel: Channel, attenuation: f32) -> Result { if attenuation > 31.5 || attenuation < 0.0 { return Err(Error::Bounds); @@ -27,6 +41,13 @@ pub trait AttenuatorInterface { Ok(attenuation_code as f32 / 2.0) } + /// Get the attenuation of a channel. + /// + /// Args: + /// * `channel` - The channel to get the attenuation of. + /// + /// Returns: + /// The programmed attenuation of the channel in dB. fn get_attenuation(&mut self, channel: Channel) -> Result { let mut channels = [0_u8; 4]; diff --git a/stabilizer/src/pounder/mod.rs b/stabilizer/src/pounder/mod.rs index 093cfd2..7be3304 100644 --- a/stabilizer/src/pounder/mod.rs +++ b/stabilizer/src/pounder/mod.rs @@ -80,6 +80,7 @@ pub struct DdsClockConfig { } impl Into for Channel { + /// Translate pounder channels to DDS output channels. fn into(self) -> ad9959::Channel { match self { Channel::In0 => ad9959::Channel::Two, @@ -90,13 +91,21 @@ impl Into for Channel { } } +/// A structure for the QSPI interface for the DDS. pub struct QspiInterface { pub qspi: hal::qspi::Qspi, mode: ad9959::Mode, } impl QspiInterface { + /// Initialize the QSPI interface. + /// + /// Args: + /// * `qspi` - The QSPI peripheral driver. pub fn new(mut qspi: hal::qspi::Qspi) -> Result { + // This driver only supports operation in 4-bit mode due to bus inconsistencies between the + // QSPI peripheral and the DDS. Instead, we will bit-bang communications in + // single-bit-two-wire to the DDS to configure it to 4-bit operation. qspi.configure_mode(hal::qspi::QspiMode::FourBit).map_err(|_| Error::Qspi)?; Ok(Self { qspi: qspi, mode: ad9959::Mode::SingleBitTwoWire }) } @@ -105,12 +114,21 @@ impl QspiInterface { impl ad9959::Interface for QspiInterface { type Error = Error; + /// Configure the operations mode of the interface. + /// + /// Args: + /// * `mode` - The newly desired operational mode. fn configure_mode(&mut self, mode: ad9959::Mode) -> Result<(), Error> { self.mode = mode; Ok(()) } + /// Write data over QSPI to the DDS. + /// + /// Args: + /// * `addr` - The address to write over QSPI to the DDS. + /// * `data` - The data to write. fn write(&mut self, addr: u8, data: &[u8]) -> Result<(), Error> { if (addr & 0x80) != 0 { return Err(Error::InvalidAddress); @@ -206,6 +224,7 @@ impl ad9959::Interface for QspiInterface { } } +/// A structure containing implementation for Pounder hardware. pub struct PounderDevices { pub ad9959: ad9959::Ad9959 PounderDevices where DELAY: embedded_hal::blocking::delay::DelayMs, { + /// Construct and initialize pounder-specific hardware. + /// + /// Args: + /// * `ad9959` - The DDS driver for the pounder hardware. + /// * `attenuator_spi` - A SPI interface to control digital attenuators. + /// * `adc1` - The ADC1 peripheral for measuring power. + /// * `adc2` - The ADC2 peripheral for measuring power. + /// * `adc1_in_p` - The input channel for the RF power measurement on IN0. + /// * `adc2_in_p` - The input channel for the RF power measurement on IN1. pub fn new(mcp23017: mcp23017::MCP23017>, ad9959: ad9959::Ad9959 Result<(), Error>{ self.mcp23017.digital_write(EXT_CLK_SEL_PIN, true).map_err(|_| Error::I2c)?; self.ad9959.configure_system_clock(frequency, prescaler).map_err(|_| Error::Dds)?; @@ -263,13 +296,21 @@ where Ok(()) } - fn select_onboard_clock(&mut self, prescaler: u8) -> Result<(), Error> { + /// Select the onboard oscillator for the DDS reference clock source. + /// + /// Args: + /// * `multiplier` - The multiplier of the reference clock to use in the DDS. + fn select_onboard_clock(&mut self, multiplier: u8) -> Result<(), Error> { self.mcp23017.digital_write(EXT_CLK_SEL_PIN, false).map_err(|_| Error::I2c)?; - self.ad9959.configure_system_clock(100_000_000f32, prescaler).map_err(|_| Error::Dds)?; + self.ad9959.configure_system_clock(100_000_000f32, multiplier).map_err(|_| Error::Dds)?; Ok(()) } + /// Configure the Pounder DDS clock. + /// + /// Args: + /// * `config` - The configuration of the DDS clock desired. pub fn configure_dds_clock(&mut self, config: DdsClockConfig) -> Result<(), Error> { if config.external_clock { self.select_external_clock(config.reference_clock, config.multiplier) @@ -278,6 +319,10 @@ where } } + /// Get the pounder DDS clock configuration + /// + /// Returns: + /// The current pounder DDS clock configuration. pub fn get_dds_clock_config(&mut self) -> Result { let external_clock = self.mcp23017.digital_read(EXT_CLK_SEL_PIN).map_err(|_| Error::I2c)?; let multiplier = self.ad9959.get_reference_clock_multiplier().map_err(|_| Error::Dds)?; @@ -286,6 +331,13 @@ where Ok(DdsClockConfig{multiplier, reference_clock, external_clock}) } + /// Get the state of a Pounder input channel. + /// + /// Args: + /// * `channel` - The pounder channel to get the state of. Must be an input channel + /// + /// Returns: + /// The read-back channel input state. pub fn get_input_channel_state(&mut self, channel: Channel) -> Result { match channel { Channel::In0 | Channel::In1 => { @@ -304,6 +356,13 @@ where } } + /// Get the state of a DDS channel. + /// + /// Args: + /// * `channel` - The pounder channel to get the state of. + /// + /// Returns: + /// The read-back channel state. fn get_dds_channel_state(&mut self, channel: Channel) -> Result { let frequency = self.ad9959.get_frequency(channel.into()).map_err(|_| Error::Dds)?; let phase_offset = self.ad9959.get_phase(channel.into()).map_err(|_| Error::Dds)?; @@ -313,6 +372,13 @@ where Ok(DdsChannelState {phase_offset, frequency, amplitude, enabled}) } + /// Get the state of a DDS output channel. + /// + /// Args: + /// * `channel` - The pounder channel to get the output state of. Must be an output channel. + /// + /// Returns: + /// The read-back output channel state. pub fn get_output_channel_state(&mut self, channel: Channel) -> Result { match channel { Channel::Out0 | Channel::Out1 => { @@ -328,6 +394,11 @@ where } } + /// Configure a DDS channel. + /// + /// Args: + /// * `channel` - The pounder channel to configure. + /// * `state` - The state to configure the channel for. pub fn set_channel_state(&mut self, channel: Channel, state: ChannelState) -> Result<(), Error> { self.ad9959.set_frequency(channel.into(), state.parameters.frequency).map_err(|_| Error::Dds)?; self.ad9959.set_phase(channel.into(), state.parameters.phase_offset).map_err(|_| Error::Dds)?; @@ -347,6 +418,7 @@ where impl AttenuatorInterface for PounderDevices { + /// Reset all of the attenuators to a power-on default state. fn reset_attenuators(&mut self) -> Result<(), Error> { self.mcp23017.digital_write(ATT_RST_N_PIN, false).map_err(|_| Error::I2c)?; // TODO: Measure the I2C transaction speed to the RST pin to ensure that the delay is @@ -356,6 +428,10 @@ impl AttenuatorInterface for PounderDevices Ok(()) } + /// Latch a configuration into a digital attenuator. + /// + /// Args: + /// * `channel` - The attenuator channel to latch. fn latch_attenuators(&mut self, channel: Channel) -> Result<(), Error> { let pin = match channel { Channel::In0 => ATT_LE0_PIN, @@ -372,12 +448,20 @@ impl AttenuatorInterface for PounderDevices Ok(()) } + /// Read the raw attenuation codes stored in the attenuator shift registers. + /// + /// Args: + /// * `channels` - A slice to store the channel readings into. fn read_all_attenuators(&mut self, channels: &mut [u8; 4]) -> Result<(), Error> { self.attenuator_spi.transfer(channels).map_err(|_| Error::Spi)?; Ok(()) } + /// Write the attenuator shift registers. + /// + /// Args: + /// * `channels` - The data to write into the attenuators. fn write_all_attenuators(&mut self, channels: &[u8; 4]) -> Result<(), Error> { let mut result = [0_u8; 4]; result.clone_from_slice(channels); @@ -388,6 +472,13 @@ impl AttenuatorInterface for PounderDevices } impl PowerMeasurementInterface for PounderDevices { + /// Sample an ADC channel. + /// + /// Args: + /// * `channel` - The channel to sample. + /// + /// Returns: + /// The sampled voltage of the specified channel. fn sample_converter(&mut self, channel: Channel) -> Result { let adc_scale = match channel { Channel::In0 => { diff --git a/stabilizer/src/pounder/rf_power.rs b/stabilizer/src/pounder/rf_power.rs index 31e6bd4..ffe30f1 100644 --- a/stabilizer/src/pounder/rf_power.rs +++ b/stabilizer/src/pounder/rf_power.rs @@ -1,8 +1,15 @@ use super::{Error, Channel}; +/// Provide an interface to measure RF input power in dB. pub trait PowerMeasurementInterface { fn sample_converter(&mut self, channel: Channel) -> Result; + /// Measure the power of an inpu channel in dB. + /// + /// Note: This function assumes the input channel is connected to an AD8363 output. + /// + /// Args: + /// * `channel` - The pounder channel to measure the power of. fn measure_power(&mut self, channel: Channel) -> Result { let analog_measurement = self.sample_converter(channel)?; diff --git a/stabilizer/src/server.rs b/stabilizer/src/server.rs index 1a21dfb..7361592 100644 --- a/stabilizer/src/server.rs +++ b/stabilizer/src/server.rs @@ -63,6 +63,7 @@ impl<'a> Request<'a> { impl Response { + /// Remove all double quotation marks from the `value` field of a response. fn sanitize_value(&mut self) { let mut new_value: String = String::new(); for byte in self.value.as_str().chars() { @@ -76,6 +77,8 @@ impl Response { self.value = new_value; } + /// Remove all double quotation marks from the `value` field of a response and wrap it in single + /// quotes. fn wrap_and_sanitize_value(&mut self) { let mut new_value: String = String::new(); new_value.push('\'').unwrap(); @@ -91,6 +94,13 @@ impl Response { self.value = new_value; } + /// Construct a successful reply. + /// + /// Note: `value` will be sanitized to convert all single quotes to double quotes. + /// + /// Args: + /// * `attrbute` - The attribute of the success. + /// * `value` - The value of the attribute. pub fn success<'a, 'b>(attribute: &'a str, value: &'b str) -> Self { let mut res = Self { code: 200, attribute: String::from(attribute), value: String::from(value)}; @@ -98,6 +108,13 @@ impl Response { res } + /// Construct an error reply. + /// + /// Note: `message` will be sanitized to convert all single quotes to double quotes. + /// + /// Args: + /// * `attrbute` - The attribute of the success. + /// * `message` - The message denoting the error. pub fn error<'a, 'b>(attribute: &'a str, message: &'b str) -> Self { let mut res = Self { code: 400, attribute: String::from(attribute), value: String::from(message)}; @@ -105,6 +122,13 @@ impl Response { res } + /// Construct a custom reply. + /// + /// Note: `message` will be sanitized to convert all single quotes to double quotes. + /// + /// Args: + /// * `attrbute` - The attribute of the success. + /// * `message` - The message denoting the status. pub fn custom<'a>(code: i32, message : &'a str) -> Self { let mut res = Self { code: code, attribute: String::from(""), value: String::from(message)}; @@ -134,6 +158,7 @@ pub struct Server { } impl Server { + /// Construct a new server object for managing requests. pub fn new() -> Self { Self { data: Vec::new(), @@ -141,6 +166,11 @@ impl Server { } } + /// Poll the server for potential data updates. + /// + /// Args: + /// * `socket` - The socket to check contents from. + /// * `f` - A closure that can be called if a request has been received on the server. pub fn poll( &mut self, socket: &mut net::socket::TcpSocket, @@ -173,6 +203,10 @@ impl Server { let r = from_slice::(&self.data[..self.data.len() - 1]); match r { Ok(mut res) => { + // Note that serde_json_core doesn't escape quotations within a string. + // To account for this, we manually translate all single quotes to + // double quotes. This occurs because we doubly-serialize this field in + // some cases. res.restore_value(); let response = f(&res); json_reply(socket, &response);