-
Notifications
You must be signed in to change notification settings - Fork 23
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
Control Pounder through MQTT #610
Conversation
It is just to avoid index confusion. The datasheet starts the index from 0 as well.
Added the same Pounder telemetry to @jordens What are the plans of Pounder for |
src/hardware/pounder/mod.rs
Outdated
pub ch: [PDHChannel; 2], | ||
} | ||
|
||
impl Default for PDHLockGeneratorConfig { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Derive it.
There are no plans for Pounder and |
- Packed pounder telemetries into `PounderTelemetry`. - Moved CPU temperature & Pounder telemetries into the telemetry buffer.
Just to make sense of variable accessing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking much better! Most of my comments are related to documentation and clarifying magic numbers in various files.
There's quite a few things Robert pointed out that still need clean up as well
ad9959/src/lib.rs
Outdated
// TODO: Update / disable any enabled channels? | ||
let mut fr1: [u8; 3] = [0, 0, 0]; | ||
self.read(Register::FR1, &mut fr1)?; | ||
fr1[0].set_bits(2..=6, multiplier); | ||
|
||
let vco_range = frequency > 255e6; | ||
let vco_range = frequency > 200e6; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please move the 200e6 to a const
variable with a name at the top of the file. Also, can you please document where this limit is coming from? It's not clear to me (nor can I remember why the original 255e6 was selected)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documented. Its selection is somewhat arbitrary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I talk about this below, but we should just use the min of the high VCO gain config or max of the low VCO gain config constants instead of this arbitrary value.
src/hardware/setup.rs
Outdated
if self.pounder.get_attenuation(pounder_channel).unwrap() | ||
!= channel_config.attenuation | ||
{ | ||
self.pounder | ||
.set_attenuation( | ||
pounder_channel, | ||
channel_config.attenuation, | ||
) | ||
.unwrap(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see any downside to unconditionally writing the attenuation
(even if it's redundant). Is there any transient that occurs when we latch the attenuation? If not, I'd simplify this and just always write attenuation updates.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then should we just have a function that sets attenuation of all channels in 1 go? We are currently repeating the SPI write (read + update) 4 times when only 1 write is needed.
Then we don't need to think about preserving other channel's attenuation with a read sequence, since we are writing unconditionally.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It really just comes down to how often we're updating attenuation and when we do it. If we need the efficiency, then by all means write all of them in one go, but if it doesn't matter, lets leave what we have in place and open an issue for the future
Co-authored-by: Ryan Summers <summers.ryan.m@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking better, but there's still a few things to clean up. Thanks for working on this. I think there's also some outstanding conversations form @jordens and I earlier as well that still need to be addressed.
/// An arbitrary frequency that sits between the 160 MHz to 255 MHz zone, where system clock | ||
/// frequency is not supported by both high frequency and low frequency VCO settings. | ||
const VCO_MIN_FREQUENCY: f32 = 200e6; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't allow configuration in the 160-255MHz range based on the clocking checks below. We should remove this constant and just use either the min (high gain) or max (low gain) as a switch for the VCO gain bit.
/// The maximum system clock frequency with low frequency range VCO. | ||
const MAX_OUTPUT_FREQUENCY: f32 = 160e6; | ||
/// The minimum system clock frequency with low frequency range VCO. | ||
const MIN_OUTPUT_FREQUENCY: f32 = 100e6; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// The maximum system clock frequency with low frequency range VCO. | |
const MAX_OUTPUT_FREQUENCY: f32 = 160e6; | |
/// The minimum system clock frequency with low frequency range VCO. | |
const MIN_OUTPUT_FREQUENCY: f32 = 100e6; | |
/// The system clock frequency range with low gain configured for the internal VCO. | |
const LOW_GAIN_VCO_RANGE: Range<f32> = core::ops::Range { start: 100e6, end: 160e6 }; |
/// The maximum system clock frequency with high frequency range VCO. | ||
const MAX_VCO_OUTPUT_FREQUENCY: f32 = 500e6; | ||
/// The minimum system clock frequency with high frequency range VCO. | ||
const MIN_VCO_OUTPUT_FREQUENCY: f32 = 255e6; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// The maximum system clock frequency with high frequency range VCO. | |
const MAX_VCO_OUTPUT_FREQUENCY: f32 = 500e6; | |
/// The minimum system clock frequency with high frequency range VCO. | |
const MIN_VCO_OUTPUT_FREQUENCY: f32 = 255e6; | |
/// The system clock frequency range with high gain configured for the internal VCO. | |
const HIGH_GAIN_VCO_RANGE: Range<f32> = core::ops::Range { start: 255e6, end: 500e6 }; |
// The REFCLK frequency must be at least 1 MHz with REFCLK multiplier disabled. | ||
if multiplier == 1 && reference_clock_frequency < MIN_REFCLK_FREQUENCY { | ||
return Err(Error::Bounds); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this to the top of the function and remove the check for multiplier == 1
. In reality, 1MHz is the absolute lowest allowed REFCLK, but the limit might be higher. We never should allow a lower refclk.
/// Convert amplitude into amplitude control register values. | ||
/// | ||
/// Arguments: | ||
/// * `amplitude` - The amplitude scaling factor of a DDS channel. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// * `amplitude` - The amplitude scaling factor of a DDS channel. | |
/// * `amplitude` - The normalized amplitude of a DDS channel. |
let amplitude_control: u16 = (amplitude * (1 << 10) as f32) as u16; | ||
|
||
// Enable the amplitude multiplier for the channel if required. The amplitude control has | ||
// full-scale at 0x3FF (amplitude of 1), so the multiplier should be disabled whenever | ||
// full-scale is used. | ||
let acr = if amplitude != 1.0 { | ||
// Enable the amplitude multiplier | ||
(amplitude_control & 0x3FF) | (1 << 12) | ||
} else { | ||
0 | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let amplitude_control: u16 = (amplitude * (1 << 10) as f32) as u16; | |
// Enable the amplitude multiplier for the channel if required. The amplitude control has | |
// full-scale at 0x3FF (amplitude of 1), so the multiplier should be disabled whenever | |
// full-scale is used. | |
let acr = if amplitude != 1.0 { | |
// Enable the amplitude multiplier | |
(amplitude_control & 0x3FF) | (1 << 12) | |
} else { | |
0 | |
}; | |
let acr: u32 = 0u32.set_bits(0..=9, (amplitude * (1 << 10) as f32) as u16).set_bit(12, amplitude != 1.0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the bit_field
API: https://docs.rs/bit_field/0.10.1/bit_field/
@@ -199,6 +214,7 @@ mod app { | |||
adcs: (Adc0Input, Adc1Input), | |||
dacs: (Dac0Output, Dac1Output), | |||
iir_state: [[iir::Vec5<f32>; IIR_CASCADE_LENGTH]; 2], | |||
dds_clock_state: Option<ClockConfig>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This never actually changes in the code. Why do we store it as a Resource
? It seems like this should just exist within Pounder
instead.
pub temperature: f32, | ||
|
||
/// The detected RF power into IN channels | ||
pub input_powers: [f32; 2], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pub input_powers: [f32; 2], | |
pub input_power: [f32; 2], |
It's already plural because it's an array, naming it input_powers
implies each channel has multiple power measurements
/// overhead. | ||
#[derive(Copy, Clone, Serialize)] | ||
pub struct PounderTelemetry { | ||
/// The Pounder temperature in degree Celsius |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// The Pounder temperature in degree Celsius | |
/// The Pounder temperature in degrees Celsius |
impl PounderDevices { | ||
pub fn update_dds( | ||
&mut self, | ||
settings: PounderConfig, | ||
clocking: &mut ClockConfig, | ||
) { | ||
if *clocking != settings.clock { | ||
match validate_clocking( | ||
settings.clock.reference_clock, | ||
settings.clock.multiplier, | ||
) { | ||
Ok(_frequency) => { | ||
self.pounder | ||
.set_ext_clk(settings.clock.external_clock) | ||
.unwrap(); | ||
|
||
self.dds_output | ||
.builder() | ||
.set_system_clock( | ||
settings.clock.reference_clock, | ||
settings.clock.multiplier, | ||
) | ||
.unwrap() | ||
.write(); | ||
|
||
*clocking = settings.clock; | ||
} | ||
Err(err) => { | ||
log::error!("Invalid AD9959 clocking parameters: {:?}", err) | ||
} | ||
} | ||
} | ||
|
||
for (channel_config, pounder_channel) in settings | ||
.in_channel | ||
.iter() | ||
.chain(settings.out_channel.iter()) | ||
.zip([ | ||
PounderChannel::In0, | ||
PounderChannel::In1, | ||
PounderChannel::Out0, | ||
PounderChannel::Out1, | ||
]) | ||
{ | ||
match Profile::try_from((*clocking, *channel_config)) { | ||
Ok(dds_profile) => { | ||
self.dds_output | ||
.builder() | ||
.update_channels_with_profile( | ||
pounder_channel.into(), | ||
dds_profile, | ||
) | ||
.write(); | ||
|
||
if let Err(err) = self.pounder.set_attenuation( | ||
pounder_channel, | ||
channel_config.attenuation, | ||
) { | ||
log::error!("Invalid attenuation settings: {:?}", err) | ||
} | ||
} | ||
Err(err) => { | ||
log::error!("Invalid AD9959 profile settings: {:?}", err) | ||
} | ||
} | ||
} | ||
} | ||
|
||
pub fn get_telemetry(&mut self) -> PounderTelemetry { | ||
PounderTelemetry { | ||
temperature: self.pounder.lm75.read_temperature().unwrap(), | ||
input_powers: [ | ||
self.pounder.measure_power(PounderChannel::In0).unwrap(), | ||
self.pounder.measure_power(PounderChannel::In1).unwrap(), | ||
], | ||
} | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This belongs in hardware/pounder/
somewhere, not the setup utilities.
Closed in favor of #725 |
Description
This PR implements control of devices on Pounder through MQTT.
These parameters are configurable through MQTT (topic prefix:
<prefix>/settings/pdh/ch/<n>
).Temperature reading from Pounder (LM75) & input powers (after attenuators) are reported in telemetry.
I also tried reporting power level through the LED, but I got issues like AttRstN being stuck at low voltage. I have another branch in my fork that has working LED using the mcp23017 library.