Skip to content

Commit

Permalink
Updating documentation, refactoring reflected power interlock threshold
Browse files Browse the repository at this point in the history
  • Loading branch information
ryan-summers committed Jan 4, 2021
1 parent 862ce55 commit 84eea81
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 84 deletions.
31 changes: 28 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,27 +52,52 @@ jobs:
- beta
steps:
- uses: actions/checkout@v2

- name: Install Rust ${{ matrix.toolchain }}
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.toolchain }}
target: thumbv7em-none-eabihf
override: true
- name: cargo check
components: llvm-tools-preview

- name: Install Binutils
uses: actions-rs/cargo@v1
with:
command: install
args: cargo-binutils

- name: Style Check
uses: actions-rs/cargo@v1
with:
command: check
args: --verbose
- name: cargo build

- name: Build [Debug]
uses: actions-rs/cargo@v1
with:
command: build
- name: cargo build release

- name: Build [Release]
uses: actions-rs/cargo@v1
with:
command: build
args: --release

- name: Generate Release
uses: actions-rs/cargo@v1
with:
command: objcopy
args: --release --verbose -- -O binary booster-release.bin

- name: Upload Release
if: ${{ matrix.toolchain == 'stable' }}
with:
name: Firmware Images
path: |
target/*/release/booster
booster-release.bin
compile-unstable:
runs-on: ubuntu-latest
steps:
Expand Down
27 changes: 24 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,12 @@ Additionally, the USB port allows the user to:
* Reset booster
* Enter DFU mode for upgrading firmware

## Ethernet
## Booster Units

Booster uses SI units for all telemetry messages and properties, with the exception of power
measurements. All power measurements are instead specified in dBm.

## Ethernet Telemetry and Control

Booster uses MQTT for telemetry and control of RF channels. All booster MQTT topics are prefixed
with the booster ID, which defaults to a combination of `booster` and the MAC address of the device.
Expand All @@ -127,6 +132,18 @@ property is provided, Booster will respond by default to `<ID>/log`.

For a reference implementation of the API to control booster over MQTT, refer to `booster.py`

**Note**: When using mosquitto, the channel telemetry can be read using:
```
mosquitto_sub -h mqtt -t '<ID>/ch<N>'
```
Note in the above that <N> is an integer between 0 and 7 (inclusive). <ID> is as specified above.

### Booster Properties

Throughout the code, any reference to a "Property" refers to a value that is configurable by the
user. Telemetry outputs, such as temperature or power measurements, are not considered to be
properties of Booster.

### Special Note on Booster properties

When reading or writing Booster properties of `<ID>/channel/read` or `<ID>/channel/write`, the
Expand Down Expand Up @@ -214,7 +231,11 @@ the channel when Booster boots. Note that saving channel settings overwrites any
**Prerequisites**
* Ensure `dfu-util` is installed. On Ubuntu, install it from `apt` using `sudo apt-get install
dfu-util`
* If building your own firmware, install `cargo-binutils`: `cargo install cargo-binutils`
* If building your own firmware, [`cargo-binutils`](https://github.com/rust-embedded/cargo-binutils#installation)) must be installed:
```
cargo install cargo-binutils
rustup component add llvm-tools-preview
```

The following instructions describe the process of uploading a new firmware image over the DFU
Bootloader USB interface.
Expand All @@ -224,7 +245,7 @@ Bootloader USB interface.
- Note: You may also use the latest [pre-built](https://github.com/quartiq/booster/releases) assets instead of building firmware.

1. Generate the binary file for your firmware build: `cargo objcopy -- -O binary booster.bin`
- Note: If you built with `--release`, replace `debug` with `release` in the above command.
- Note: If you built with `--release`, use the commmand: `cargo objcopy --release -- -O binary booster.bin`

1. Reset Booster into DFU mode:
- Insert a pin into the DFU Bootloader hole to press the DFU button
Expand Down
19 changes: 7 additions & 12 deletions booster.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

class PropertyId(enum.Enum):
""" Represents a property ID for Booster RF channels. """
InterlockThresholds = 'InterlockThresholds'
OutputInterlockThreshold = 'OutputInterlockThreshold'
OutputPowerTransform = 'OutputPowerTransform'
InputPowerTransform = 'InputPowerTransform'
ReflectedPowerTransform = 'ReflectedPowerTransform'
Expand Down Expand Up @@ -273,14 +273,11 @@ async def channel_configuration(args):
await interface.enable_channel(args.channel)
print(f'Channel {args.channel} enabled')

if args.thresholds:
thresholds = {
'output': args.thresholds[0],
'reflected': args.thresholds[1],
}
await interface.write_property(args.channel, PropertyId.InterlockThresholds, thresholds)
print(f'Channel {args.channel}: Output power threshold = {args.thresholds[0]} dBm, '
f'Reflected power interlock threshold = {args.thresholds[1]} dBm')
if args.threshold:
await interface.write_property(args.channel,
PropertyId.OutputInterlockThreshold,
args.threshold)
print(f'Channel {args.channel}: Output power threshold = {args.threshold} dBm')

if args.bias:
vgs, ids = await interface.set_bias(args.channel, args.bias)
Expand Down Expand Up @@ -317,9 +314,7 @@ def main():
help='Tune the RF channel bias current to the provided value')
parser.add_argument('--enable', action='store_true', help='Enable the RF channel')
parser.add_argument('--disable', action='store_true', help='Disable the RF channel')
parser.add_argument('--thresholds', type=float, nargs=2,
help='The interlock thresholds in the following order: '
'<output> <reflected>')
parser.add_argument('--threshold', type=float, help='The output interlock threshold')
parser.add_argument('--save', action='store_true', help='Save the RF channel configuration')

loop = asyncio.get_event_loop()
Expand Down
17 changes: 8 additions & 9 deletions src/mqtt_control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ use embedded_hal::blocking::delay::DelayUs;
use heapless::{consts, String};
use minimq::{Property, QoS};

use crate::rf_channel::{
InterlockThresholds, Property as ChannelProperty, PropertyId as ChannelPropertyId,
};
use crate::rf_channel::{Property as ChannelProperty, PropertyId as ChannelPropertyId};

use crate::linear_transformation::LinearTransformation;

Expand All @@ -37,8 +35,8 @@ impl PropertyReadResponse {
pub fn okay(prop: ChannelProperty) -> String<consts::U256> {
// Serialize the property.
let data: String<consts::U64> = match prop {
ChannelProperty::InterlockThresholds(thresholds) => {
serde_json_core::to_string(&thresholds).unwrap()
ChannelProperty::OutputInterlockThreshold(threshold) => {
serde_json_core::to_string(&threshold).unwrap()
}
ChannelProperty::InputPowerTransform(transform) => {
serde_json_core::to_string(&transform).unwrap()
Expand Down Expand Up @@ -95,10 +93,11 @@ impl PropertyWriteRequest {

// Convert the property
let prop = match self.prop {
ChannelPropertyId::InterlockThresholds => ChannelProperty::InterlockThresholds(
serde_json_core::from_str::<InterlockThresholds>(&data)
.map_err(|_| Error::Invalid)?,
),
ChannelPropertyId::OutputInterlockThreshold => {
ChannelProperty::OutputInterlockThreshold(
serde_json_core::from_str::<f32>(&data).map_err(|_| Error::Invalid)?,
)
}
ChannelPropertyId::OutputPowerTransform => ChannelProperty::OutputPowerTransform(
serde_json_core::from_str::<LinearTransformation>(&data)
.map_err(|_| Error::Invalid)?,
Expand Down
4 changes: 4 additions & 0 deletions src/platform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ use super::hal;

use embedded_hal::{blocking::delay::DelayUs, digital::v2::OutputPin};

// Booster hardware channels are capable of withstanding up to 1W of reflected RF power. This
// corresponds with a value of 30 dBm.
pub const MAXIMUM_REFLECTED_POWER_DBM: f32 = 30.0;

#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
cortex_m::interrupt::disable();
Expand Down
88 changes: 33 additions & 55 deletions src/rf_channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,18 @@ use stm32f4xx_hal::{

use rtic::cyccnt::{Duration, Instant};

#[derive(serde::Deserialize, serde::Serialize)]
/// Represents the interlock threshold levels.
pub struct InterlockThresholds {
pub output: f32,
pub reflected: f32,
}

#[derive(serde::Deserialize, serde::Serialize)]
/// Used to identify a specific channel property.
pub enum PropertyId {
InterlockThresholds,
OutputInterlockThreshold,
OutputPowerTransform,
InputPowerTransform,
ReflectedPowerTransform,
}

/// Represents an operation property of an RF channel.
pub enum Property {
InterlockThresholds(InterlockThresholds),
OutputInterlockThreshold(f32),
OutputPowerTransform(LinearTransformation),
InputPowerTransform(LinearTransformation),
ReflectedPowerTransform(LinearTransformation),
Expand Down Expand Up @@ -514,12 +507,17 @@ impl RfChannel {
};

channel
.set_interlock_thresholds(
.set_output_interlock_threshold(
channel.settings.data.output_interlock_threshold,
channel.settings.data.reflected_interlock_threshold,
)
.unwrap();

// The reflected power interlock threshold is always configured to 30 dBm (1W
// reflected power) to protect Booster hardware.
channel
.set_reflected_interlock_threshold(platform::MAXIMUM_REFLECTED_POWER_DBM)
.unwrap();

// If the channel configuration specifies the channel as enabled, power up the
// channel now.
if channel.settings.data.enabled && platform::watchdog_detected() == false {
Expand All @@ -543,44 +541,35 @@ impl RfChannel {
/// Set the interlock thresholds for the channel.
///
/// # Args
/// * `output_power` - The dBm interlock threshold to configure for the output power.
/// * `reflected_power` - The dBm interlock threshold to configure for reflected power.
pub fn set_interlock_thresholds(
&mut self,
output_power: f32,
reflected_power: f32,
) -> Result<(), Error> {
/// * `power` - The dBm interlock threshold to configure for reflected power.
fn set_reflected_interlock_threshold(&mut self, power: f32) -> Result<(), Error> {
match self.i2c_devices.interlock_thresholds_dac.set_voltage(
self.settings
.data
.reflected_power_transform
.invert(reflected_power),
self.settings.data.reflected_power_transform.invert(power),
ad5627::Dac::A,
) {
Err(ad5627::Error::Range) => return Err(Error::Bounds),
Err(ad5627::Error::I2c(_)) => return Err(Error::Interface),
Ok(voltage) => {
self.settings.data.reflected_interlock_threshold =
self.settings.data.reflected_power_transform.map(voltage);
}
Err(ad5627::Error::Range) => Err(Error::Bounds),
Err(ad5627::Error::I2c(_)) => Err(Error::Interface),
Ok(_) => Ok(()),
}
}

/// Set the output interlock threshold for the channel.
///
/// # Args
/// * `power` - The dBm interlock threshold to configure for the output power.
pub fn set_output_interlock_threshold(&mut self, power: f32) -> Result<(), Error> {
match self.i2c_devices.interlock_thresholds_dac.set_voltage(
self.settings
.data
.output_power_transform
.invert(output_power),
self.settings.data.output_power_transform.invert(power),
ad5627::Dac::B,
) {
Err(ad5627::Error::Range) => return Err(Error::Bounds),
Err(ad5627::Error::I2c(_)) => return Err(Error::Interface),
Err(ad5627::Error::Range) => Err(Error::Bounds),
Err(ad5627::Error::I2c(_)) => Err(Error::Interface),
Ok(voltage) => {
self.settings.data.output_interlock_threshold =
self.settings.data.output_power_transform.map(voltage);
Ok(())
}
}

Ok(())
}

fn check_faults(&mut self) -> Option<ChannelFault> {
Expand Down Expand Up @@ -745,10 +734,8 @@ impl RfChannel {
// As a workaround, we need to ensure that the interlock level is above the output power
// detector level. When RF is disabled, the power detectors output a near-zero value, so
// 100mV should be a sufficient level.
if (self.settings.data.reflected_interlock_threshold
< self.settings.data.reflected_power_transform.map(0.100))
|| (self.settings.data.output_interlock_threshold
< self.settings.data.output_power_transform.map(0.100))
if self.settings.data.output_interlock_threshold
< self.settings.data.output_power_transform.map(0.100)
{
self.start_disable();
return Err(Error::Invalid);
Expand Down Expand Up @@ -947,14 +934,6 @@ impl RfChannel {
self.settings.data.output_interlock_threshold
}

/// Get the current reflected power interlock threshold.
///
/// # Returns
/// The current reflected interlock threshold in dBm.
pub fn get_reflected_interlock_threshold(&self) -> f32 {
self.settings.data.reflected_interlock_threshold
}

/// Get the current bias voltage programmed to the RF amplification transistor.
pub fn get_bias_voltage(&self) -> f32 {
self.settings.data.bias_voltage
Expand All @@ -975,7 +954,7 @@ impl RfChannel {
input_power: self.get_input_power(),
output_power: self.get_output_power(adc),
reflected_power: self.get_reflected_power(adc),
reflected_overdrive_threshold: self.get_reflected_interlock_threshold(),
reflected_overdrive_threshold: platform::MAXIMUM_REFLECTED_POWER_DBM,
output_overdrive_threshold: self.get_output_interlock_threshold(),
bias_voltage: self.get_bias_voltage(),
state: self.get_state(),
Expand All @@ -995,8 +974,8 @@ impl RfChannel {
/// * `property` - The property to configure on the channel.
pub fn set_property(&mut self, property: Property) -> Result<(), Error> {
match property {
Property::InterlockThresholds(InterlockThresholds { output, reflected }) => {
self.set_interlock_thresholds(output, reflected)?;
Property::OutputInterlockThreshold(output) => {
self.set_output_interlock_threshold(output)?;
}
Property::OutputPowerTransform(transform) => {
self.settings.data.output_power_transform = transform;
Expand All @@ -1021,10 +1000,9 @@ impl RfChannel {
/// The current channel property.
pub fn get_property(&self, property: PropertyId) -> Property {
match property {
PropertyId::InterlockThresholds => Property::InterlockThresholds(InterlockThresholds {
output: self.settings.data.output_interlock_threshold,
reflected: self.settings.data.reflected_interlock_threshold,
}),
PropertyId::OutputInterlockThreshold => {
Property::OutputInterlockThreshold(self.settings.data.output_interlock_threshold)
}
PropertyId::OutputPowerTransform => {
Property::OutputPowerTransform(self.settings.data.output_power_transform.clone())
}
Expand Down
2 changes: 0 additions & 2 deletions src/settings/channel_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use microchip_24aa02e48::Microchip24AA02E48;
/// Represents booster channel-specific configuration values.
#[derive(serde::Serialize, serde::Deserialize)]
pub struct BoosterChannelData {
pub reflected_interlock_threshold: f32,
pub output_interlock_threshold: f32,
pub bias_voltage: f32,
pub enabled: bool,
Expand All @@ -25,7 +24,6 @@ impl BoosterChannelData {
/// Generate default booster channel data.
pub fn default() -> Self {
Self {
reflected_interlock_threshold: 0.0,
output_interlock_threshold: 0.0,
bias_voltage: -3.2,
enabled: false,
Expand Down

0 comments on commit 84eea81

Please sign in to comment.