Skip to content

xwr.radar

Interface implementation for generic TI demo MSS firmware.

Usage

After selecting the appropriate XWRXXXX interface:

  1. Initialization parameters can be defaults. The port may need to be changed if multiple radars are being used, or another device uses the /dev/ttyACM0 default name. The baudrate should not be changed.
  2. Setup with .setup(...) with the desired radar configuration.
  3. Start the radar with .start().
  4. Stop the radar with .stop().

Info

You may need to provide read/write permissions to access the serial port:

sudo chmod 777 /dev/ttyACM0  # or whatever port the radar is on.

Danger

If the configuration is invalid, .start() may return an error, or cause the radar to freeze. This may require the radar to be rebooted via manually disconnecting the power supply.

xwr.radar.AWR1642

Bases: XWRBase, APIMixins

Interface implementation for the TI AWR1642 family.

Supported devices

  • AWR1642Boost

Parameters:

Name Type Description Default
port str | None

radar control serial port; typically the lower numbered one.

None
baudrate int

baudrate of control port.

115200
name str

human-readable name.

'AWR1642'
Source code in src/xwr/radar/api.py
class AWR1642(XWRBase, common.APIMixins):
    """Interface implementation for the TI AWR1642 family.

    !!! info "Supported devices"

        - AWR1642Boost

    Args:
        port: radar control serial port; typically the lower numbered one.
        baudrate: baudrate of control port.
        name: human-readable name.
    """

    _PORT_NAME = r'XDS110'
    _START_COMMAND = "sensorStart"
    _TX_MASK = 0b011
    _RX_MASK = 0b1111
    NUM_TX = 2
    NUM_RX = 4
    BYTES_PER_SAMPLE = 2 * 2

    def __init__(
        self, port: str | None = None, baudrate: int = 115200,
        name: str = "AWR1642"
    ) -> None:
        super().__init__(port=port, baudrate=baudrate, name=name)

    def setup(
        self, frequency: float = 77.0, idle_time: float = 110.0,
        adc_start_time: float = 4.0, ramp_end_time: float = 56.0,
        tx_start_time: float = 1.0, freq_slope: float = 70.006,
        adc_samples: int = 256, sample_rate: int = 5000,
        frame_length: int = 64, frame_period: float = 100.0
    ) -> None:
        """Configure radar.

        Args:
            frequency: frequency band, in GHz; 77.0 or 76.0.
            idle_time: see TI chirp timing documentation; in us.
            adc_start_time: see TI chirp timing documentation; in us.
            ramp_end_time: see TI chirp timing documentation; in us.
            tx_start_time: see TI chirp timing documentation; in us.
            freq_slope: chirp frequency slope; in MHz/us.
            adc_samples: number of samples per chirp.
            sample_rate: ADC sampling rate; in ksps.
            frame_length: chirps per frame per TX antenna. Must be a power of 2.
            frame_period: time between the start of each frame; in ms.
        """
        assert frame_length & (frame_length - 1) == 0

        self.stop()
        self.send("flushCfg")
        self.send("dfeDataOutputMode {modeType.value}".format(
            modeType=defines.DFEMode.LEGACY))
        self.send(common.configure_adc(adc_fmt=defines.ADCFormat.COMPLEX_1X))
        self.profileCfg(
            startFreq=frequency, idleTime=idle_time,
            adcStartTime=adc_start_time, rampEndTime=ramp_end_time,
            txStartTime=tx_start_time, freqSlopeConst=freq_slope,
            numAdcSamples=adc_samples, digOutSampleRate=sample_rate)

        self.send(common.configure_channels(rx=self._RX_MASK, tx=self._TX_MASK))
        self.frameCfg(
            numLoops=frame_length, chirpEndIdx=self.NUM_TX - 1,
            framePeriodicity=frame_period)
        self.compRangeBiasAndRxChanPhase(rx_phase = [(0, 1)] * 4 * 2)
        self.send("bpmCfg -1 0 0 1")
        self.lvdsStreamCfg()

        # For some reason, the AWR1642 requires adcMode=1.
        # Not sure what this does.
        self.send("lowPower 0 1")

        self.send(common.get_boilerplate())
        self.log.info("Radar setup complete.")

setup

setup(
    frequency: float = 77.0,
    idle_time: float = 110.0,
    adc_start_time: float = 4.0,
    ramp_end_time: float = 56.0,
    tx_start_time: float = 1.0,
    freq_slope: float = 70.006,
    adc_samples: int = 256,
    sample_rate: int = 5000,
    frame_length: int = 64,
    frame_period: float = 100.0,
) -> None

Configure radar.

Parameters:

Name Type Description Default
frequency float

frequency band, in GHz; 77.0 or 76.0.

77.0
idle_time float

see TI chirp timing documentation; in us.

110.0
adc_start_time float

see TI chirp timing documentation; in us.

4.0
ramp_end_time float

see TI chirp timing documentation; in us.

56.0
tx_start_time float

see TI chirp timing documentation; in us.

1.0
freq_slope float

chirp frequency slope; in MHz/us.

70.006
adc_samples int

number of samples per chirp.

256
sample_rate int

ADC sampling rate; in ksps.

5000
frame_length int

chirps per frame per TX antenna. Must be a power of 2.

64
frame_period float

time between the start of each frame; in ms.

100.0
Source code in src/xwr/radar/api.py
def setup(
    self, frequency: float = 77.0, idle_time: float = 110.0,
    adc_start_time: float = 4.0, ramp_end_time: float = 56.0,
    tx_start_time: float = 1.0, freq_slope: float = 70.006,
    adc_samples: int = 256, sample_rate: int = 5000,
    frame_length: int = 64, frame_period: float = 100.0
) -> None:
    """Configure radar.

    Args:
        frequency: frequency band, in GHz; 77.0 or 76.0.
        idle_time: see TI chirp timing documentation; in us.
        adc_start_time: see TI chirp timing documentation; in us.
        ramp_end_time: see TI chirp timing documentation; in us.
        tx_start_time: see TI chirp timing documentation; in us.
        freq_slope: chirp frequency slope; in MHz/us.
        adc_samples: number of samples per chirp.
        sample_rate: ADC sampling rate; in ksps.
        frame_length: chirps per frame per TX antenna. Must be a power of 2.
        frame_period: time between the start of each frame; in ms.
    """
    assert frame_length & (frame_length - 1) == 0

    self.stop()
    self.send("flushCfg")
    self.send("dfeDataOutputMode {modeType.value}".format(
        modeType=defines.DFEMode.LEGACY))
    self.send(common.configure_adc(adc_fmt=defines.ADCFormat.COMPLEX_1X))
    self.profileCfg(
        startFreq=frequency, idleTime=idle_time,
        adcStartTime=adc_start_time, rampEndTime=ramp_end_time,
        txStartTime=tx_start_time, freqSlopeConst=freq_slope,
        numAdcSamples=adc_samples, digOutSampleRate=sample_rate)

    self.send(common.configure_channels(rx=self._RX_MASK, tx=self._TX_MASK))
    self.frameCfg(
        numLoops=frame_length, chirpEndIdx=self.NUM_TX - 1,
        framePeriodicity=frame_period)
    self.compRangeBiasAndRxChanPhase(rx_phase = [(0, 1)] * 4 * 2)
    self.send("bpmCfg -1 0 0 1")
    self.lvdsStreamCfg()

    # For some reason, the AWR1642 requires adcMode=1.
    # Not sure what this does.
    self.send("lowPower 0 1")

    self.send(common.get_boilerplate())
    self.log.info("Radar setup complete.")

xwr.radar.AWR1843

Bases: XWRBase, APIMixins

Interface implementation for the TI AWR1843 family.

Supported devices

  • AWR1843Boost
  • AWR1843AOPEVM

Parameters:

Name Type Description Default
port str | None

radar control serial port; typically the lower numbered one.

None
baudrate int

baudrate of control port.

115200
name str

human-readable name.

'AWR1843'
Source code in src/xwr/radar/api.py
class AWR1843(XWRBase, common.APIMixins):
    """Interface implementation for the TI AWR1843 family.

    !!! info "Supported devices"

        - AWR1843Boost
        - AWR1843AOPEVM

    Args:
        port: radar control serial port; typically the lower numbered one.
        baudrate: baudrate of control port.
        name: human-readable name.
    """

    _PORT_NAME = r'(?=.*CP2105)(?=.*Enhanced)|XDS110'
    _START_COMMAND = "sensorStart"
    _TX_MASK = 0b111
    _RX_MASK = 0b1111
    NUM_TX = 3
    NUM_RX = 4
    BYTES_PER_SAMPLE = 2 * 2

    def __init__(
        self, port: str | None = None, baudrate: int = 115200,
        name: str = "AWR1843"
    ) -> None:
        super().__init__(port=port, baudrate=baudrate, name=name)

    def setup(
        self, frequency: float = 77.0, idle_time: float = 110.0,
        adc_start_time: float = 4.0, ramp_end_time: float = 56.0,
        tx_start_time: float = 1.0, freq_slope: float = 70.006,
        adc_samples: int = 256, sample_rate: int = 5000,
        frame_length: int = 64, frame_period: float = 100.0
    ) -> None:
        """Configure radar.

        Args:
            frequency: frequency band, in GHz; 77.0 or 76.0.
            idle_time: see TI chirp timing documentation; in us.
            adc_start_time: see TI chirp timing documentation; in us.
            ramp_end_time: see TI chirp timing documentation; in us.
            tx_start_time: see TI chirp timing documentation; in us.
            freq_slope: chirp frequency slope; in MHz/us.
            adc_samples: number of samples per chirp.
            sample_rate: ADC sampling rate; in ksps.
            frame_length: chirps per frame per TX antenna. Must be a power of 2.
            frame_period: time between the start of each frame; in ms.
        """
        assert frame_length & (frame_length - 1) == 0

        self.stop()
        self.send("flushCfg")
        self.send("dfeDataOutputMode {modeType.value}".format(
            modeType=defines.DFEMode.LEGACY))
        self.send(common.configure_adc(adc_fmt=defines.ADCFormat.COMPLEX_1X))
        self.profileCfg(
            startFreq=frequency, idleTime=idle_time,
            adcStartTime=adc_start_time, rampEndTime=ramp_end_time,
            txStartTime=tx_start_time, freqSlopeConst=freq_slope,
            numAdcSamples=adc_samples, digOutSampleRate=sample_rate)

        self.send(common.configure_channels(rx=self._RX_MASK, tx=self._TX_MASK))
        self.frameCfg(
            numLoops=frame_length, chirpEndIdx=self.NUM_TX - 1,
            framePeriodicity=frame_period)
        self.compRangeBiasAndRxChanPhase(rx_phase = [(0, 1)] * 4 * 3)
        self.lvdsStreamCfg()

        self.send("lowPower 0 0")
        self.send(common.get_boilerplate())
        self.log.info("Radar setup complete.")

setup

setup(
    frequency: float = 77.0,
    idle_time: float = 110.0,
    adc_start_time: float = 4.0,
    ramp_end_time: float = 56.0,
    tx_start_time: float = 1.0,
    freq_slope: float = 70.006,
    adc_samples: int = 256,
    sample_rate: int = 5000,
    frame_length: int = 64,
    frame_period: float = 100.0,
) -> None

Configure radar.

Parameters:

Name Type Description Default
frequency float

frequency band, in GHz; 77.0 or 76.0.

77.0
idle_time float

see TI chirp timing documentation; in us.

110.0
adc_start_time float

see TI chirp timing documentation; in us.

4.0
ramp_end_time float

see TI chirp timing documentation; in us.

56.0
tx_start_time float

see TI chirp timing documentation; in us.

1.0
freq_slope float

chirp frequency slope; in MHz/us.

70.006
adc_samples int

number of samples per chirp.

256
sample_rate int

ADC sampling rate; in ksps.

5000
frame_length int

chirps per frame per TX antenna. Must be a power of 2.

64
frame_period float

time between the start of each frame; in ms.

100.0
Source code in src/xwr/radar/api.py
def setup(
    self, frequency: float = 77.0, idle_time: float = 110.0,
    adc_start_time: float = 4.0, ramp_end_time: float = 56.0,
    tx_start_time: float = 1.0, freq_slope: float = 70.006,
    adc_samples: int = 256, sample_rate: int = 5000,
    frame_length: int = 64, frame_period: float = 100.0
) -> None:
    """Configure radar.

    Args:
        frequency: frequency band, in GHz; 77.0 or 76.0.
        idle_time: see TI chirp timing documentation; in us.
        adc_start_time: see TI chirp timing documentation; in us.
        ramp_end_time: see TI chirp timing documentation; in us.
        tx_start_time: see TI chirp timing documentation; in us.
        freq_slope: chirp frequency slope; in MHz/us.
        adc_samples: number of samples per chirp.
        sample_rate: ADC sampling rate; in ksps.
        frame_length: chirps per frame per TX antenna. Must be a power of 2.
        frame_period: time between the start of each frame; in ms.
    """
    assert frame_length & (frame_length - 1) == 0

    self.stop()
    self.send("flushCfg")
    self.send("dfeDataOutputMode {modeType.value}".format(
        modeType=defines.DFEMode.LEGACY))
    self.send(common.configure_adc(adc_fmt=defines.ADCFormat.COMPLEX_1X))
    self.profileCfg(
        startFreq=frequency, idleTime=idle_time,
        adcStartTime=adc_start_time, rampEndTime=ramp_end_time,
        txStartTime=tx_start_time, freqSlopeConst=freq_slope,
        numAdcSamples=adc_samples, digOutSampleRate=sample_rate)

    self.send(common.configure_channels(rx=self._RX_MASK, tx=self._TX_MASK))
    self.frameCfg(
        numLoops=frame_length, chirpEndIdx=self.NUM_TX - 1,
        framePeriodicity=frame_period)
    self.compRangeBiasAndRxChanPhase(rx_phase = [(0, 1)] * 4 * 3)
    self.lvdsStreamCfg()

    self.send("lowPower 0 0")
    self.send(common.get_boilerplate())
    self.log.info("Radar setup complete.")

xwr.radar.AWR1843L

Bases: AWR1843

TI AWR1843Boost with its middle antenna disabled.

Supported devices

  • AWR1843Boost, with the middle TX antenna which is 1/2-wavelength above the other two disabled.

Parameters:

Name Type Description Default
port str | None

radar control serial port; typically the lower numbered one.

None
baudrate int

baudrate of control port.

115200
name str

human-readable name.

'AWR1843'
Source code in src/xwr/radar/api.py
class AWR1843L(AWR1843):
    """TI AWR1843Boost with its middle antenna disabled.

    !!! info "Supported devices"

        - AWR1843Boost, with the middle TX antenna which is 1/2-wavelength
          above the other two disabled.

    Args:
        port: radar control serial port; typically the lower numbered one.
        baudrate: baudrate of control port.
        name: human-readable name.
    """

    _PORT_NAME = r"XDS110"
    _START_COMMAND = "sensorStart"
    _TX_MASK = 0b101
    _RX_MASK = 0b1111
    NUM_TX = 2
    NUM_RX = 4
    BYTES_PER_SAMPLE = 2 * 2

setup

setup(
    frequency: float = 77.0,
    idle_time: float = 110.0,
    adc_start_time: float = 4.0,
    ramp_end_time: float = 56.0,
    tx_start_time: float = 1.0,
    freq_slope: float = 70.006,
    adc_samples: int = 256,
    sample_rate: int = 5000,
    frame_length: int = 64,
    frame_period: float = 100.0,
) -> None

Configure radar.

Parameters:

Name Type Description Default
frequency float

frequency band, in GHz; 77.0 or 76.0.

77.0
idle_time float

see TI chirp timing documentation; in us.

110.0
adc_start_time float

see TI chirp timing documentation; in us.

4.0
ramp_end_time float

see TI chirp timing documentation; in us.

56.0
tx_start_time float

see TI chirp timing documentation; in us.

1.0
freq_slope float

chirp frequency slope; in MHz/us.

70.006
adc_samples int

number of samples per chirp.

256
sample_rate int

ADC sampling rate; in ksps.

5000
frame_length int

chirps per frame per TX antenna. Must be a power of 2.

64
frame_period float

time between the start of each frame; in ms.

100.0
Source code in src/xwr/radar/api.py
def setup(
    self, frequency: float = 77.0, idle_time: float = 110.0,
    adc_start_time: float = 4.0, ramp_end_time: float = 56.0,
    tx_start_time: float = 1.0, freq_slope: float = 70.006,
    adc_samples: int = 256, sample_rate: int = 5000,
    frame_length: int = 64, frame_period: float = 100.0
) -> None:
    """Configure radar.

    Args:
        frequency: frequency band, in GHz; 77.0 or 76.0.
        idle_time: see TI chirp timing documentation; in us.
        adc_start_time: see TI chirp timing documentation; in us.
        ramp_end_time: see TI chirp timing documentation; in us.
        tx_start_time: see TI chirp timing documentation; in us.
        freq_slope: chirp frequency slope; in MHz/us.
        adc_samples: number of samples per chirp.
        sample_rate: ADC sampling rate; in ksps.
        frame_length: chirps per frame per TX antenna. Must be a power of 2.
        frame_period: time between the start of each frame; in ms.
    """
    assert frame_length & (frame_length - 1) == 0

    self.stop()
    self.send("flushCfg")
    self.send("dfeDataOutputMode {modeType.value}".format(
        modeType=defines.DFEMode.LEGACY))
    self.send(common.configure_adc(adc_fmt=defines.ADCFormat.COMPLEX_1X))
    self.profileCfg(
        startFreq=frequency, idleTime=idle_time,
        adcStartTime=adc_start_time, rampEndTime=ramp_end_time,
        txStartTime=tx_start_time, freqSlopeConst=freq_slope,
        numAdcSamples=adc_samples, digOutSampleRate=sample_rate)

    self.send(common.configure_channels(rx=self._RX_MASK, tx=self._TX_MASK))
    self.frameCfg(
        numLoops=frame_length, chirpEndIdx=self.NUM_TX - 1,
        framePeriodicity=frame_period)
    self.compRangeBiasAndRxChanPhase(rx_phase = [(0, 1)] * 4 * 3)
    self.lvdsStreamCfg()

    self.send("lowPower 0 0")
    self.send(common.get_boilerplate())
    self.log.info("Radar setup complete.")

xwr.radar.AWR2944

Bases: XWRBase, APIMixins

Interface implementation for the TI AWR2944.

Supported devices

  • AWR2944EVM

Parameters:

Name Type Description Default
port str | None

radar control serial port; typically the lower numbered one.

None
baudrate int

baudrate of control port.

115200
name str

human-readable name.

'AWR2944'
Source code in src/xwr/radar/api.py
class AWR2944(XWRBase, common.APIMixins):
    """Interface implementation for the TI AWR2944.

    !!! info "Supported devices"

        - AWR2944EVM

    Args:
        port: radar control serial port; typically the lower numbered one.
        baudrate: baudrate of control port.
        name: human-readable name.
    """

    _PORT_NAME = r'XDS110'
    _START_COMMAND = "sensorStart"
    _TX_MASK = 0b1111
    _RX_MASK = 0b1111
    NUM_TX = 4
    NUM_RX = 4
    BYTES_PER_SAMPLE = 2

    def __init__(
        self, port: str | None = None, baudrate: int = 115200,
        name: str = "AWR2944"
    ) -> None:
        super().__init__(port=port, baudrate=baudrate, name=name)

    def setup(
        self, frequency: float = 77.0, idle_time: float = 110.0,
        adc_start_time: float = 4.0, ramp_end_time: float = 56.0,
        tx_start_time: float = 1.0, freq_slope: float = 70.006,
        adc_samples: int = 256, sample_rate: int = 5000,
        frame_length: int = 64, frame_period: float = 100.0
    ) -> None:
        """Configure radar.

        Args:
            frequency: frequency band, in GHz; 77.0 or 76.0.
            idle_time: see TI chirp timing documentation; in us.
            adc_start_time: see TI chirp timing documentation; in us.
            ramp_end_time: see TI chirp timing documentation; in us.
            tx_start_time: see TI chirp timing documentation; in us.
            freq_slope: chirp frequency slope; in MHz/us.
            adc_samples: number of samples per chirp.
            sample_rate: ADC sampling rate; in ksps.
            frame_length: chirps per frame per TX antenna. Must be a power of 2.
            frame_period: time between the start of each frame; in ms.
        """
        assert frame_length & (frame_length - 1) == 0

        self.port.write('\n'.encode('ascii'))
        self._wait_for_response()

        self.stop()
        self.send("flushCfg")

        self.send("dfeDataOutputMode {modeType.value}".format(
            modeType=defines.DFEMode.LEGACY))

        self.send(common.configure_adc(adc_fmt=defines.ADCFormat.REAL))
        self.profileCfg(
            startFreq=frequency, idleTime=idle_time,
            adcStartTime=adc_start_time, rampEndTime=ramp_end_time,
            txStartTime=tx_start_time, freqSlopeConst=freq_slope,
            numAdcSamples=adc_samples, digOutSampleRate=sample_rate)
        self.send((
            "frameCfg {chirpStartIdx} {chirpEndIdx} {numLoops} {numFrames} "
            "{numAdcSamples} {framePeriodicity} "
            "{triggerSelect} "      # 1 = software trigger
            "{frameTriggerDelay}"   # Undocumented
        ).format(
            chirpStartIdx=0, chirpEndIdx=3, numLoops=frame_length,
            numFrames=0, numAdcSamples=adc_samples,
            framePeriodicity=frame_period,
            triggerSelect=1, frameTriggerDelay=0.0))

        self.send(common.configure_channels(rx=self._RX_MASK, tx=self._TX_MASK))
        self.compRangeBiasAndRxChanPhase(rx_phase = [(1, 0)] * 4 * 4)
        self.lvdsStreamCfg()

        # Can't be bothered to figure out what this does
        self.send(
            "antGeometryCfg 1 0 1 1 1 2 1 3 0 2 0 3 0 4 0 5 1 4 1 5 1 6 1 7 "
            "1 8 1 9 1 10 1 11 0.5 0.8")

        self.send("lowPower 0 0")
        self.send(common.get_boilerplate())

        self.log.info("Radar setup complete.")

setup

setup(
    frequency: float = 77.0,
    idle_time: float = 110.0,
    adc_start_time: float = 4.0,
    ramp_end_time: float = 56.0,
    tx_start_time: float = 1.0,
    freq_slope: float = 70.006,
    adc_samples: int = 256,
    sample_rate: int = 5000,
    frame_length: int = 64,
    frame_period: float = 100.0,
) -> None

Configure radar.

Parameters:

Name Type Description Default
frequency float

frequency band, in GHz; 77.0 or 76.0.

77.0
idle_time float

see TI chirp timing documentation; in us.

110.0
adc_start_time float

see TI chirp timing documentation; in us.

4.0
ramp_end_time float

see TI chirp timing documentation; in us.

56.0
tx_start_time float

see TI chirp timing documentation; in us.

1.0
freq_slope float

chirp frequency slope; in MHz/us.

70.006
adc_samples int

number of samples per chirp.

256
sample_rate int

ADC sampling rate; in ksps.

5000
frame_length int

chirps per frame per TX antenna. Must be a power of 2.

64
frame_period float

time between the start of each frame; in ms.

100.0
Source code in src/xwr/radar/api.py
def setup(
    self, frequency: float = 77.0, idle_time: float = 110.0,
    adc_start_time: float = 4.0, ramp_end_time: float = 56.0,
    tx_start_time: float = 1.0, freq_slope: float = 70.006,
    adc_samples: int = 256, sample_rate: int = 5000,
    frame_length: int = 64, frame_period: float = 100.0
) -> None:
    """Configure radar.

    Args:
        frequency: frequency band, in GHz; 77.0 or 76.0.
        idle_time: see TI chirp timing documentation; in us.
        adc_start_time: see TI chirp timing documentation; in us.
        ramp_end_time: see TI chirp timing documentation; in us.
        tx_start_time: see TI chirp timing documentation; in us.
        freq_slope: chirp frequency slope; in MHz/us.
        adc_samples: number of samples per chirp.
        sample_rate: ADC sampling rate; in ksps.
        frame_length: chirps per frame per TX antenna. Must be a power of 2.
        frame_period: time between the start of each frame; in ms.
    """
    assert frame_length & (frame_length - 1) == 0

    self.port.write('\n'.encode('ascii'))
    self._wait_for_response()

    self.stop()
    self.send("flushCfg")

    self.send("dfeDataOutputMode {modeType.value}".format(
        modeType=defines.DFEMode.LEGACY))

    self.send(common.configure_adc(adc_fmt=defines.ADCFormat.REAL))
    self.profileCfg(
        startFreq=frequency, idleTime=idle_time,
        adcStartTime=adc_start_time, rampEndTime=ramp_end_time,
        txStartTime=tx_start_time, freqSlopeConst=freq_slope,
        numAdcSamples=adc_samples, digOutSampleRate=sample_rate)
    self.send((
        "frameCfg {chirpStartIdx} {chirpEndIdx} {numLoops} {numFrames} "
        "{numAdcSamples} {framePeriodicity} "
        "{triggerSelect} "      # 1 = software trigger
        "{frameTriggerDelay}"   # Undocumented
    ).format(
        chirpStartIdx=0, chirpEndIdx=3, numLoops=frame_length,
        numFrames=0, numAdcSamples=adc_samples,
        framePeriodicity=frame_period,
        triggerSelect=1, frameTriggerDelay=0.0))

    self.send(common.configure_channels(rx=self._RX_MASK, tx=self._TX_MASK))
    self.compRangeBiasAndRxChanPhase(rx_phase = [(1, 0)] * 4 * 4)
    self.lvdsStreamCfg()

    # Can't be bothered to figure out what this does
    self.send(
        "antGeometryCfg 1 0 1 1 1 2 1 3 0 2 0 3 0 4 0 5 1 4 1 5 1 6 1 7 "
        "1 8 1 9 1 10 1 11 0.5 0.8")

    self.send("lowPower 0 0")
    self.send(common.get_boilerplate())

    self.log.info("Radar setup complete.")

xwr.radar.AWRL6844

Bases: XWRBase

Interface implementation for the TI AWRL6844.

Supported devices

  • AWRL6844EVM

Parameters:

Name Type Description Default
port str | None

radar control serial port; typically the lower numbered one.

None
baudrate int

baudrate of control port.

115200
name str

human-readable name.

'AWRL6844'
Source code in src/xwr/radar/api.py
class AWRL6844(XWRBase):
    """Interface implementation for the TI AWRL6844.

    !!! info "Supported devices"

        - AWRL6844EVM

    Args:
        port: radar control serial port; typically the lower numbered one.
        baudrate: baudrate of control port.
        name: human-readable name.
    """

    _PORT_NAME = r'XDS110'
    _START_COMMAND = "sensorStart 0 0 0 0"

    NUM_TX = 4
    NUM_RX = 4
    BYTES_PER_SAMPLE = 2

    def __init__(
        self, port: str | None = None, baudrate: int = 115200,
        name: str = "AWRL6844"
    ) -> None:
        super().__init__(port=port, baudrate=baudrate, name=name)

    def stop(self) -> None:
        self.send("sensorStop 0")
        self.log.info("Radar Stopped.")

    def setup(
        self, frequency: float = 60.0, idle_time: float = 110.0,
        adc_start_time: float = 4.0, ramp_end_time: float = 56.0,
        tx_start_time: float = 1.0, freq_slope: float = 70.006,
        adc_samples: int = 256, sample_rate: int = 5000,
        frame_length: int = 64, frame_period: float = 100.0
    ) -> None:
        """Configure radar.

        Args:
            frequency: chirp start frequency, in GHz.
            idle_time: chirp idle time; in us.
            adc_start_time: ADC start offset, in us.
            ramp_end_time: chirp ramp end time; in us.
            tx_start_time: TX start time offset; in us.
            freq_slope: chirp frequency slope; in MHz/us.
            adc_samples: number of ADC samples per chirp.
            sample_rate: ADC sampling rate; in ksps.
            frame_length: bursts per frame (= chirps per TX antenna per frame).
                Must be a power of 2.
            frame_period: time between the start of each frame; in ms.
        """
        assert frame_length & (frame_length - 1) == 0

        self.port.write('\n'.encode('ascii'))
        self._wait_for_response()

        self.stop()

        self.send("channelCfg 153 255 0")
        self.send("apllFreqShiftEn 0")

        # digOutputSampRate: clock divider for the 400 MHz APLL.
        # ADC rate (Msps) = 400 / (2 * digOutputSampRate), so:
        # digOutputSampRate = 400_000 / (2 * sample_rate_ksps)
        #                   = 200_000 // sample_rate
        dig_output_samp_rate = 200_000 // sample_rate

        # chirpAdcStartTime takes skip samples (int), not µs.
        adc_start_skip = round(adc_start_time * sample_rate / 1000)

        # burstPeriodus: repetition interval for one burst of NUM_TX chirps.
        # Minimum = NUM_TX * (idle_time + ramp_end_time).
        burst_period = self.NUM_TX * (idle_time + ramp_end_time)

        # chirpTxMimoPatSel=4 is MIMO_TDM_PATTERN (one TX per chirp, cycling).
        self.send(
            f"chirpComnCfg {dig_output_samp_rate} 0 0 "
            f"{adc_samples} 1 {ramp_end_time} 0")
        self.send(
            f"chirpTimingCfg {idle_time} {adc_start_skip} "
            f"{tx_start_time} {freq_slope} {frequency}")

        self.send("adcDataDitherCfg  1")

        # numOfChirpsInBurst=NUM_TX cycles through all TX antennas per burst.
        # numOfBurstsInFrame=frame_length gives frame_length chirps per TX.
        # Constraint: (NUM_TX * frame_length) % NUM_TX == 0 always holds.
        self.send(
            f"frameCfg {frame_length * 4} 0 {burst_period} "
            f"1 {frame_period} 0")

        self.send("gpAdcMeasConfig 0 0")
        self.send("guiMonitor 1 1 0 0 0 1")
        self.send("cfarProcCfg 0 2 8 4 3 0 9.0 0")
        self.send("cfarProcCfg 1 2 4 2 2 1 9.0 0")
        self.send("cfarFovCfg 0 0.25 9.0")
        self.send("cfarFovCfg 1 -20.16 20.16")
        self.send("aoaProcCfg 64 64")
        self.send("aoaFovCfg -60 60 -60 60")
        self.send("clutterRemoval 0")

        self.send("runtimeCalibCfg 1")
        self.send("antGeometryBoard xWRL6844EVM")
        self.send("adcLogging 1")
        self.send("lowPowerCfg 0")

        self.log.info("Radar setup complete.")

setup

setup(
    frequency: float = 60.0,
    idle_time: float = 110.0,
    adc_start_time: float = 4.0,
    ramp_end_time: float = 56.0,
    tx_start_time: float = 1.0,
    freq_slope: float = 70.006,
    adc_samples: int = 256,
    sample_rate: int = 5000,
    frame_length: int = 64,
    frame_period: float = 100.0,
) -> None

Configure radar.

Parameters:

Name Type Description Default
frequency float

chirp start frequency, in GHz.

60.0
idle_time float

chirp idle time; in us.

110.0
adc_start_time float

ADC start offset, in us.

4.0
ramp_end_time float

chirp ramp end time; in us.

56.0
tx_start_time float

TX start time offset; in us.

1.0
freq_slope float

chirp frequency slope; in MHz/us.

70.006
adc_samples int

number of ADC samples per chirp.

256
sample_rate int

ADC sampling rate; in ksps.

5000
frame_length int

bursts per frame (= chirps per TX antenna per frame). Must be a power of 2.

64
frame_period float

time between the start of each frame; in ms.

100.0
Source code in src/xwr/radar/api.py
def setup(
    self, frequency: float = 60.0, idle_time: float = 110.0,
    adc_start_time: float = 4.0, ramp_end_time: float = 56.0,
    tx_start_time: float = 1.0, freq_slope: float = 70.006,
    adc_samples: int = 256, sample_rate: int = 5000,
    frame_length: int = 64, frame_period: float = 100.0
) -> None:
    """Configure radar.

    Args:
        frequency: chirp start frequency, in GHz.
        idle_time: chirp idle time; in us.
        adc_start_time: ADC start offset, in us.
        ramp_end_time: chirp ramp end time; in us.
        tx_start_time: TX start time offset; in us.
        freq_slope: chirp frequency slope; in MHz/us.
        adc_samples: number of ADC samples per chirp.
        sample_rate: ADC sampling rate; in ksps.
        frame_length: bursts per frame (= chirps per TX antenna per frame).
            Must be a power of 2.
        frame_period: time between the start of each frame; in ms.
    """
    assert frame_length & (frame_length - 1) == 0

    self.port.write('\n'.encode('ascii'))
    self._wait_for_response()

    self.stop()

    self.send("channelCfg 153 255 0")
    self.send("apllFreqShiftEn 0")

    # digOutputSampRate: clock divider for the 400 MHz APLL.
    # ADC rate (Msps) = 400 / (2 * digOutputSampRate), so:
    # digOutputSampRate = 400_000 / (2 * sample_rate_ksps)
    #                   = 200_000 // sample_rate
    dig_output_samp_rate = 200_000 // sample_rate

    # chirpAdcStartTime takes skip samples (int), not µs.
    adc_start_skip = round(adc_start_time * sample_rate / 1000)

    # burstPeriodus: repetition interval for one burst of NUM_TX chirps.
    # Minimum = NUM_TX * (idle_time + ramp_end_time).
    burst_period = self.NUM_TX * (idle_time + ramp_end_time)

    # chirpTxMimoPatSel=4 is MIMO_TDM_PATTERN (one TX per chirp, cycling).
    self.send(
        f"chirpComnCfg {dig_output_samp_rate} 0 0 "
        f"{adc_samples} 1 {ramp_end_time} 0")
    self.send(
        f"chirpTimingCfg {idle_time} {adc_start_skip} "
        f"{tx_start_time} {freq_slope} {frequency}")

    self.send("adcDataDitherCfg  1")

    # numOfChirpsInBurst=NUM_TX cycles through all TX antennas per burst.
    # numOfBurstsInFrame=frame_length gives frame_length chirps per TX.
    # Constraint: (NUM_TX * frame_length) % NUM_TX == 0 always holds.
    self.send(
        f"frameCfg {frame_length * 4} 0 {burst_period} "
        f"1 {frame_period} 0")

    self.send("gpAdcMeasConfig 0 0")
    self.send("guiMonitor 1 1 0 0 0 1")
    self.send("cfarProcCfg 0 2 8 4 3 0 9.0 0")
    self.send("cfarProcCfg 1 2 4 2 2 1 9.0 0")
    self.send("cfarFovCfg 0 0.25 9.0")
    self.send("cfarFovCfg 1 -20.16 20.16")
    self.send("aoaProcCfg 64 64")
    self.send("aoaFovCfg -60 60 -60 60")
    self.send("clutterRemoval 0")

    self.send("runtimeCalibCfg 1")
    self.send("antGeometryBoard xWRL6844EVM")
    self.send("adcLogging 1")
    self.send("lowPowerCfg 0")

    self.log.info("Radar setup complete.")

xwr.radar.XWRBase

Generic AWR Interface for the TI demo MSS firmware.

The interface is based on a UART ASCII CLI, and is documented by the following sources:

Warning

We only implement a partial API. Non-mandatory calls which do not affect the LVDS raw I/Q stream are not implemented.

Info

If the radar serial port is not provided, we auto-detect the port by fetching the lowest-numbered one which contains "XDS110" in the USB device description (Or "CP2105 ... Enhanced" in the case of the AWR1843AOPEVM), which corresponds to the TI XDS110 JTAG debugger embedded in each radar dev board.

Parameters:

Name Type Description Default
port str | None

radar control serial port; typically the lower numbered one. If not provided (None), we attempt to auto-detect the port.

None
baudrate int

baudrate of control port.

115200
name str

human-readable name.

'AWR1843'

Attributes:

Name Type Description
NUM_TX int

number of TX antennas.

NUM_RX int

number of RX antennas.

BYTES_PER_SAMPLE int

number of bytes per ADC sample.

Source code in src/xwr/radar/base.py
class XWRBase:
    """Generic AWR Interface for the TI demo MSS firmware.

    The interface is based on a UART ASCII CLI, and is documented by the
    following sources:

    - The `packages/ti/demo/xwr18xx/mmw` folder in the mmWave SDK install.
    - [mmWave SDK user guide, Table 1 (Page 19)](
        https://dr-download.ti.com/software-development/software-development-kit-sdk/MD-PIrUeCYr3X/03.06.00.00-LTS/mmwave_sdk_user_guide.pdf)
    - [mmWave Studio](https://www.ti.com/tool/MMWAVE-STUDIO)
    - [AWR1843 Data Sheet](
        https://www.ti.com/lit/ds/symlink/awr1843.pdf?ts=1708800208074)

    !!! warning

        We only implement a partial API. Non-mandatory calls which do not
        affect the LVDS raw I/Q stream are not implemented.

    !!! info

        If the radar serial port is not provided, we auto-detect the port by
        fetching the lowest-numbered one which contains "XDS110" in the USB
        device description (Or "CP2105 ... Enhanced" in the case of the
        AWR1843AOPEVM), which corresponds to the [TI XDS110 JTAG debugger](
        https://www.ti.com/tool/TMDSEMU110-U) embedded in each radar dev board.

    Args:
        port: radar control serial port; typically the lower numbered one. If
            not provided (`None`), we attempt to auto-detect the port.
        baudrate: baudrate of control port.
        name: human-readable name.

    Attributes:
        NUM_TX: number of TX antennas.
        NUM_RX: number of RX antennas.
        BYTES_PER_SAMPLE: number of bytes per ADC sample.
    """

    # -- internal constants --

    # Demo firmware command prompt string
    _CMD_PROMPT = "mmwDemo:/>"
    # UART device name regex to search for when auto-detecting the radar device
    _PORT_NAME = r"XDS110"
    # Command used to start the radar
    _START_COMMAND = "sensorStart"
    # Command used to stop the radar
    _STOP_COMMAND = "sensorStop"

    # -- public constants --

    NUM_TX: int = 3
    NUM_RX: int = 4
    BYTES_PER_SAMPLE: int = 2 * 2

    def __init__(
        self, port: str | None = None, baudrate: int = 115200,
        name: str = "AWR1843"
    ) -> None:
        self.log: logging.Logger = logging.getLogger(name=name)

        if port is None:
            port = self.__detect_port()
            self.log.info(f"Auto-detected port: {port}")

        self.port: serial.Serial = serial.Serial(port, baudrate, timeout=None)

        # Only linux supports low latency mode.
        if hasattr(self.port, 'set_low_latency_mode'):
            self.port.set_low_latency_mode(True)
        else:
            self.log.warning(
                "Low latency mode is only supported on linux. This may cause "
                "initialization to take longer than expected.")

        self.port.reset_input_buffer()
        self.port.reset_output_buffer()

    def __detect_port(self) -> str:
        sorted_ports = sorted(list_ports.comports(), key=lambda x: x.device)
        for port in sorted_ports:
            if port.description is not None:
                if re.match(self._PORT_NAME, port.description, re.IGNORECASE):
                    return port.device

        self.log.error("Failed to auto-detect radar port.")
        raise XWRError(
            "Auto-detecting the radar port (`port=None`) failed: none of the "
            f"available ports contain '{self._PORT_NAME}' in the "
            "USB description. "
            f"Available ports: {[p.device for p in list_ports.comports()]}")

    def setup_from_config(self, path: str) -> None:
        """Run raw setup from a config file."""
        with open(path) as f:
            self.send(f.read())

    def setup(self, *args, **kwargs) -> None:
        raise NotImplementedError

    def _wait_for_response(self, timeout: float = 10.0) -> bytearray:
        r"""Wait for a response and read until we get `"...\rmmwDemo:/>"`.

        Args:
            timeout: timeout, in seconds.

        Returns:
            Radar response.
        """
        rx_buf = bytearray()
        prompt = self._CMD_PROMPT.encode('utf-8')
        start = time.time()
        while not rx_buf.endswith(prompt):
            rx_buf.extend(self.port.read(self.port.in_waiting))
            if time.time() - start > timeout:
                self.log.error("Timed out while waiting for response.")
                raise TimeoutError()
        return rx_buf

    def send(self, cmd: str, timeout: float = 10.0) -> None:
        """Send message, and wait for a response.

        Args:
            cmd: command to send. If the command contains newlines, each line
                is sent separately; lines starting with `#` are treated as
                comments and not sent.
            timeout: timeout, in seconds.

        Raises:
            TimeoutError: if no response is received by the timeout.
        """
        if '\n' in cmd:
            self.log.debug("Send multi-line commands...")
            for line in cmd.split('\n'):
                if line.startswith('#'):
                    self.log.debug(line)
                elif len(line) == 0:
                    pass
                else:
                    self.send(line, timeout=timeout)
            self.log.debug("... done sending multi-line commands.")
            return

        self.log.debug("Send: {}".format(cmd))
        self.port.write((cmd + '\n').encode('ascii'))
        rx_buf = self._wait_for_response(timeout=timeout)

        # Remove all the cruft
        decoded = rx_buf.decode('utf-8', errors='replace')
        resp = (
            decoded
            .replace(self._CMD_PROMPT, '').replace(cmd, '')
            .rstrip(' ;\r\n\t').lstrip(' \n\t'))
        self.log.debug("Response: {}".format(resp))

        # Check for non-normal response
        if resp != 'Done':
            if resp.startswith("Ignored"):
                self.log.warning(resp)
            elif resp.startswith("Debug") or resp.startswith("Skipped"):
                if "Error" in resp:
                    self.log.error(resp)
            elif '*****' in resp:
                pass  # header
            else:
                self.log.error(resp)
                self.log.info(f"Raw buffer for this error was: {decoded}")
                raise XWRError(resp)

    def start(self) -> None:
        """Start radar."""
        self.send(self._START_COMMAND)
        self.log.info("Radar Started.")

    def stop(self) -> None:
        """Stop radar.

        !!! warning

            The radar may be non-responsive to commands in some conditions, for
            example if the specified timings are fairly tight (or invalid).
        """
        self.send(self._STOP_COMMAND)
        self.log.info("Radar Stopped.")

send

send(cmd: str, timeout: float = 10.0) -> None

Send message, and wait for a response.

Parameters:

Name Type Description Default
cmd str

command to send. If the command contains newlines, each line is sent separately; lines starting with # are treated as comments and not sent.

required
timeout float

timeout, in seconds.

10.0

Raises:

Type Description
TimeoutError

if no response is received by the timeout.

Source code in src/xwr/radar/base.py
def send(self, cmd: str, timeout: float = 10.0) -> None:
    """Send message, and wait for a response.

    Args:
        cmd: command to send. If the command contains newlines, each line
            is sent separately; lines starting with `#` are treated as
            comments and not sent.
        timeout: timeout, in seconds.

    Raises:
        TimeoutError: if no response is received by the timeout.
    """
    if '\n' in cmd:
        self.log.debug("Send multi-line commands...")
        for line in cmd.split('\n'):
            if line.startswith('#'):
                self.log.debug(line)
            elif len(line) == 0:
                pass
            else:
                self.send(line, timeout=timeout)
        self.log.debug("... done sending multi-line commands.")
        return

    self.log.debug("Send: {}".format(cmd))
    self.port.write((cmd + '\n').encode('ascii'))
    rx_buf = self._wait_for_response(timeout=timeout)

    # Remove all the cruft
    decoded = rx_buf.decode('utf-8', errors='replace')
    resp = (
        decoded
        .replace(self._CMD_PROMPT, '').replace(cmd, '')
        .rstrip(' ;\r\n\t').lstrip(' \n\t'))
    self.log.debug("Response: {}".format(resp))

    # Check for non-normal response
    if resp != 'Done':
        if resp.startswith("Ignored"):
            self.log.warning(resp)
        elif resp.startswith("Debug") or resp.startswith("Skipped"):
            if "Error" in resp:
                self.log.error(resp)
        elif '*****' in resp:
            pass  # header
        else:
            self.log.error(resp)
            self.log.info(f"Raw buffer for this error was: {decoded}")
            raise XWRError(resp)

setup_from_config

setup_from_config(path: str) -> None

Run raw setup from a config file.

Source code in src/xwr/radar/base.py
def setup_from_config(self, path: str) -> None:
    """Run raw setup from a config file."""
    with open(path) as f:
        self.send(f.read())

start

start() -> None

Start radar.

Source code in src/xwr/radar/base.py
def start(self) -> None:
    """Start radar."""
    self.send(self._START_COMMAND)
    self.log.info("Radar Started.")

stop

stop() -> None

Stop radar.

Warning

The radar may be non-responsive to commands in some conditions, for example if the specified timings are fairly tight (or invalid).

Source code in src/xwr/radar/base.py
def stop(self) -> None:
    """Stop radar.

    !!! warning

        The radar may be non-responsive to commands in some conditions, for
        example if the specified timings are fairly tight (or invalid).
    """
    self.send(self._STOP_COMMAND)
    self.log.info("Radar Stopped.")

xwr.radar.XWRError

Bases: Exception

Error raised by the Radar (via non-normal return message).

Source code in src/xwr/radar/base.py
class XWRError(Exception):
    """Error raised by the Radar (via non-normal return message)."""

    pass