Peregrine is an ice-penetrating radar-equipped small UAS designed to be low-cost and field portable.
This is the multi-page printable view of this section. Click here to print.
Peregrine Payload
1 - Peregrine Radar Payload Box Assembly
Bill of Materials
A complete bill of materials for the payload box is available in this Google Sheet, embedded below.
Some parts are custom made. See the Custom Parts page for suggestions on how to build these yourself or source them from reliable vendors.
Assembly instructions
Preparing enclosure halves
The payload box is a clamshell-style design with two interlocking halves. The “upper” half contains the SDR. The “lower” half holds the Raspberry Pi. Sandwiched between the two halves is the “payload divider” PCB, which houses some sensors and provides for external connections.
Starting with the Pi Shell:
- Place 4x M2.5 heat inserts into the four mounting holes at the bottom of the box.
- Place 2x M2.5 heat inserts into the two front panel mounting holes on the front of the box.
Continuing with the SDR Shell:
- Place 4x M2.5 heat inserts into the four holes in the tabs sticking up, to accept bolts from the outside to connect the two shells together.
Copper foil wrapping
Print out the cutout templates TODO LINK. The first two pages are the cutout templates for the copper foil wrapping on the top and bottom of the enclosure.
Double check the dimensions
Use a ruler to measure the marked dimensions on the first page to make sure your print is at the right scale.Spread a layer of adhesive-backed copper out on a cutting-safe surface (such as a sacrificial piece of wood) and use some masking tape to hold it down. Tape the cutout template on top. Use an X-Acto knife (or similar) to cut:
- A small “X” across each of the external bolt holes (to allow a bolt to go through – no need to try to cut out the circle itself)
- The internal cutout for the heatsink
- The exterior outline of the entire piece.
If you’re not sure which lines to cut, take a moment to see how the paper folds around the 3D printed pieces. Your goal is to fully cover the exterior.
Carefully peel away the adhesive from part of the copper foil and start pasting it onto the 3D printed piece. It’s easier to do this bit by bit, rather than pulling the entire backing off at once.
Repeat with the other half of the enclosure.
2 - External button
An external lighted push button can be added to turn the radar on and off. A connector for this is included on the Payload Divider PCB.
Button choices
The intended button for the UAV version is HB15CKW01-5C-CB.
Most other similar buttons can be used. For the ground-based version, we use ULV4F2BSS311. Some of the photos on this page are taken with this button, which can be mounted externally.
Wiring the button
The button connector is a 5 position JST GH connector. The connectors and pre-crimped wires are available separately or you can buy a kit.
If you buy pre-crimped wires, you’ll need to cut the connector on one side off.
For JST connectors, the small notch on the contact aligns with the bottom side of the connector. On the bottom of the connector housing, there are a series of small plastic pins that will raise up as you insert the contact. Once you get the contact fully inserted, this pin will fall back down to lay flush.
To assemble the button:
- Cut pre-crimped JST wires to length
- Following the image above, strip and solder the free end of each wire to the specified terminals on the switch.
- Place a piece of heatshrink over each terminal and shrink.
- (Optional) Twist the wires gently and use small pieces of heat shrink to keep them in a neat bundle.
- Insert the contacts into the JST GH 5 position housing, following the diagram above.
- Mount the button and insert the connector into the plug on the payload divider PCB.
3 - Raspberry Pi Setup
In order to standardize between units, much of the Pi setup is automated or semi-automated. This guide will walk you through the steps of setting up your Pi the way we do. Along the way, there are also links for more information on how to customize this setup. This is an area where you will almost certainly need to customize some aspects of the setup.
Initial setup with cloud-init
Setup of the Raspberry Pi is semi-automated using cloud-init.
Cloud-init customization
The cloud-init setup is controlled by two files: user-data
and network-config
.
(You’ll use these files a couple of steps down.)
Examples of each are shown below, but you will likely need to modify these to suit your purpose. We have pages on how to customize user-data and network-config.
user-data Example
#cloud-config
# This is the user-data configuration file for cloud-init.
# The cloud-init documentation has more details:
#
# https://cloudinit.readthedocs.io/
system_info:
default_user:
name: ubuntu # Allow the default user to shutdown or reboot the system without entering a password (used by our automated scripts)
sudo: "ALL=(ALL) NOPASSWD: /sbin/poweroff, /sbin/reboot, /sbin/shutdown"
# On first boot, set the (default) ubuntu user's password to "cryosphere"
chpasswd:
expire: false
list:
- ubuntu:$6$rounds=4096$aQ7tu0.beL3WAL32$fKxKYvZpY7EMCoxAU1heRomA3v8WvgbqBhhz08QwOtQdlP/DJOP2BThqZFoRW8d2a9PaIKK9BC9NHs1qNnkya1
# Enable password authentication with the SSH daemon
ssh_pwauth: true
# Set a default timezone
timezone: Etc/UTC
## On first boot, use ssh-import-id to give the specific users SSH access to
## the default user
ssh_import_id:
- gh:thomasteisberg
- gh:albroome
- gh:dfxmay
## Update apt database and upgrade packages on first boot
package_update: true
package_upgrade: true
## Install additional packages on first boot
packages:
- net-tools
- git
- cmake
- g++
- mosh
- exfat-fuse
- i2c-tools
- rpi.gpio-common
- util-linux-extra
## Write arbitrary files to the file-system
write_files:
- path: /home/ubuntu/initial_setup.sh
content: |
#!/bin/bash
exec > >(tee -a "initial_setup_output.log") 2>&1
# Miniconda Setup
wget --progress=bar:force:noscroll "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-Linux-aarch64.sh" -O $HOME/miniconda.sh
bash $HOME/miniconda.sh -b -p $HOME/miniconda
cd $HOME
source .profile
source miniconda/etc/profile.d/conda.sh
conda init bash
# Setup logger environment
git clone git@github.com:thomasteisberg/uav_radar_logger.git
# Clone uhd_radar repo
git clone git@github.com:radioglaciology/uhd_radar.git
cd uhd_radar
#git checkout thomas/dask # Uncomment if you want to check out a specific branch other than main
conda env create -n uhd -f environment-rpi.yaml
conda activate uhd
python /home/ubuntu/miniconda/envs/uhd/lib/uhd/utils/uhd_images_downloader.py
systemctl --user enable radar.service
systemctl --user enable logger.service
ifconfig
sudo reboot
append: true
- path: /home/ubuntu/.profile
content: |
PATH=/home/ubuntu/miniconda/bin:$PATH
source /home/ubuntu/.bashrc
append: true
- path: /home/ubuntu/.ssh/known_hosts
content: |
github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=
- path: /etc/security/limits.conf # Recommended by Ettus https://kb.ettus.com/USRP_Host_Performance_Tuning_Tips_and_Tricks
content: |
ubuntu - rtprio 99
append: true
- path: /etc/systemd/user/radar.service
content: |
[Unit]
Description=Service to run the radar code on startup
[Service]
Type=simple
WorkingDirectory=/home/ubuntu/uhd_radar/
ExecStart=/home/ubuntu/uhd_radar/manager/radar_service.sh
Restart=always
RestartSec=10
KillSignal=SIGINT
[Install]
WantedBy=default.target
- path: /etc/systemd/user/logger.service
content: |
[Unit]
Description=Service to log data from I2C sensors and automatically shutdown below a voltage threshold
[Service]
Type=simple
WorkingDirectory=/home/ubuntu/uav_radar_logger/
ExecStart=/home/ubuntu/uav_radar_logger/logger_service.sh
Restart=always
RestartSec=60
KillSignal=SIGINT
[Install]
WantedBy=default.target
# Run arbitrary commands at rc.local like time
# These commands are run with root permissions
# If you want commands run as a normal user, put them in initial_setup.sh (see above)
# which is run as the "ubuntu" user (see below)
runcmd:
- chown -R ubuntu:ubuntu /home/ubuntu
- chmod +x /home/ubuntu/initial_setup.sh
- wget -O /etc/udev/rules.d/uhd-usrp.rules https://raw.githubusercontent.com/EttusResearch/uhd/master/host/utils/uhd-usrp.rules
- usermod -a -G i2c ubuntu
- usermod -a -G dialout ubuntu
- usermod -a -G tty ubuntu
- apt remove -y modemmanager
- systemctl stop serial-getty@ttyS0.service && systemctl disable serial-getty@ttyS0.service
- i2cdetect -y 1
- echo "dtoverlay=i2c-rtc,pcf8523" >> /boot/firmware/config.txt
- loginctl enable-linger ubuntu
- mkdir /media/ssd
- chown ubuntu /media/ssd
- chgrp ubuntu /media/ssd
- echo "/dev/sda2 /media/ssd exfat defaults,nofail,uid=1000,gid=1000 0 2" | tee -a /etc/fstab
network-config Example
# This file contains a netplan-compatible configuration which cloud-init will
# apply on first-boot (note: it will *not* update the config after the first
# boot). Please refer to the cloud-init documentation and the netplan reference
# for full details:
#
# https://cloudinit.readthedocs.io/en/latest/topics/network-config.html
# https://cloudinit.readthedocs.io/en/latest/topics/network-config-format-v2.html
# https://netplan.io/reference
version: 2
ethernets:
eth0: # Your ethernet name.
dhcp4: no
addresses: [192.168.11.137/24]
gateway4: 192.168.11.1
nameservers:
addresses: [8.8.8.8,8.8.4.4]
wifis:
renderer: networkd
wlan0:
dhcp4: true
optional: true
access-points:
"<YOUR WIFI SSID HERE>":
password: "<YOUR WIFI PASSWORD HERE>"
Imaging your Pi
To start, download the Raspberry Pi Imager tool (or use your preferred software for imaging SD cards). On Ubuntu, you can install it like this:
sudo apt install rpi-imager
For other operating systems, see the website.
Note on selecting a microSD card
Not all (micro) SD cards are the same. Speed and reliability can both vary a lot.
Especially if you plan to store your data to your MicroSD card, you don’t want to mess around with this. Don’t use an SD card that’s off-brand, questionably sourced (i.e. possibly counterfeit), or used.
There are many websites that have Pi-specific MicroSD benchmarks.
We use Samsung Pro Plus series MicroSD cards. They are available in up to 512 GB sizes.
Launch Imager. After clicking on “Choose OS,” navigate through the general purpose
category to find Ubuntu Server 22.04.xx LTS 64-bit
. 64-bit is important – 32-bit
will not work.
Insert your MicroSD card and select it as the location to write to.
After imaging is complete, you will see two drives mounted: writable
and
system-boot
.
Copying cloud-init config files
After customizing the user-data
and network-config
files (see above),
copy user-data
and network-config
to the system-boot
volume, replacing the
existing files.
Eject the microSD and put it back in the Pi.
Running cloud-init
Your Pi needs an internet connection for this part
Make sure you Pi will have access to the internet before you begin this part. See the networking page for information.Power up the Pi and wait for cloud-init to run.
Within about a minute, your Pi should connect to whatever network interface(s)
are described in network-config
and you should be able to find it on the
network. If you setup some sort of key-based authentication (such as by
importing a key from GitHub), it may take an extra couple of minutes for
this to be ready.
After the network setup is complete, you should be able to login over SSH.
For instructions on SSH-ing into your Pi, see here. In particular, please note that you need to have SSH agent forwarding working. Instructions for this are on that page.
When you first login, cloud-init may not have finished running. To check the status, run:
cloud-init status --long
There are also logs in /var/log/cloud-init-output.log
.
To keep an eye on the entire process, you can run:
watch "cloud-init status --long && tail -n 10 /var/log/cloud-init-output.log"
Expect this process to take a few minutes to complete.
Running initial_setup.sh
After the cloud-init process is complete, you’ll also need to run
initial_setup.sh
:
./initial_setup.sh
This will log to /home/ubuntu/initial_setup_output.log
. It may take around 10
minutes to complete. It will automatically reboot your Pi at the end. If you
don’t want this, feel free to comment out the last line.
Setting up the logger service
More details on the logging service will eventually be here.
For now, if you’re building a Peregrine system, the default setup should be fine.
If you’re building Eyas, first run this command to create a place for logs:
mkdir /media/ssd/logger
And then modify the last line of /home/ubuntu/uav_radar_logger/logger_service.sh
to look like this:
python -u logger.py --shutdown-voltage 11.8 --log-dir /media/ssd/logger/
Replacing 11.8
with an appropriate threshold (in volts) at which to shutdown.
Setting up the radar service
More details on the radar service will eventually be here.
For now, if you’re building a Peregrine system, the default setup should be fine.
If you’re building Eyas, run these commands to create a location for logging data and update the default configuration:
mkdir /media/ssd/radar
cd /home/ubuntu/uhd_radar/config
mv default.yaml default-old.yaml
cp default-eyas.yaml default.yaml
Using overlayroot to protect the file system
Let’s take a quick step back and talk about the problem before we talk about (perhaps drastic) solutions:
MicroSD card corruption is an ongoing challenge for any device (such as the Pi) that boots from a MicroSD card. Most cases can be traced to either (a) junk MicroSD cards or (b) sudden power loss during a write operation.
You can mostly avoid problem (a) by carefully sourcing your MicroSD cards. If you’re considering saving $20 by buying an off-brand MicroSD card or, worse, a possible fake, don’t do it.
Sudden power loss is harder to completely guard against. Ideally, you want to
shutdown your Pi before removing power. This can be done by SSH-ing into it and
running sudo shutdown -h now
and waiting about a minute. If you use our
radar systemd service, you can also perform a safe shutdown by pressing and
holding the button for 5 seconds, waiting for the light to turn off, and then
giving it about a minute to fully shut down.
For Peregrine, this should all be good enough.
For Eyas and other longer-term installations, the risk of a battery discharging faster than expected is relatively high.
The first line of defense is the automatic shutdown provided by the logger service that will safely shut everything down when the battery voltage gets too low.
If you have a separate data storage device, such as an external USB-connected SSD, you can go a step further and make your Pi’s MicroSD card read-only.
A utility called overlayroot
(which uses OverlayFS)
can be used to make the root filesystem read-only and create an “overlay filesystem”
stored only in RAM. This means that all changes to the MicroSD card file system
are temporary and are lost upon reboot.
This is great for keeping a standardized configuration, but you need to be aware that this means most log files are lost on reboot, any config files on the SD card are lost on reboot, and all radar data you want to keep around must be stored to your external storage device.
You can read a more detailed tutorial about Overlayroot here.
If you’re going to do this, be sure that you’ve tested your setup in advance so you know if the data you want to keep is being stored.
Enabling Overlayroot
If you still want to set this up, it’s quite easy to enable.
Simply edit the last line of /etc/overlayroot.conf
to this:
overlayroot="tmpfs:recurse=0"
tmpfs
specifies that we want to use a RAM-based filesystem to store the “overlay”
part of the filesystem. recurse=0
says that we want to apply this only to the
/
mount and not to other mounts below that.
(You need to edit this file as root. For example: sudo vim /etc/overlayroot.conf
)
Disabling Overlayroot
You can temporarily re-mount the root filesystem to edit things using the built-in utility:
sudo overlayroot-chroot
To exit, just type exit
.
If you want to permenantly disable it, just edit the last line of /etc/overlayroot.conf
back to:
overlayroot=""
and reboot.
3.1 - Connecting to the Raspberry Pi
By default, the cloud-init script sets up a static IP of 192.168.11.137
, but
you could choose to configure this to something different for each payload box.
Our usual way of connecting is by plugging an ethernet cable into the Pi and connecting it to a laptop. You can read about all the networking options here.
SSH agent forwarding
If you need to authenticate to GitHub to download code, the recommended way is using SSH agent forwarding.
To summarize the above link, you should make sure you have an SSH key setup with your GitHub account.
Test this by running ssh -T git@github.com
.
Then modify your ~/.ssh/config
file to have an entry like this enabling
SSH agent forwarding:
Host 192.168.11.137
HostName 192.168.11.137
User ubuntu
ForwardAgent yes
SSH’ing to your Pi
Connect to the Raspberry Pi over SSH:
ssh ubuntu@192.168.11.137
Remote host identification has changed
If you use the same static IP for multiple Pi’s (which, presumably, you only ever use one of at a time), you may encounter an error stating:
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
This is because your computer thinks its connecting to a different Raspberry Pi.
You can manually add the fingerprint for the currently connected Pi like this:
ssh-keyscan -H 192.168.11.137 >> ~/.ssh/known_hosts
(This would be a bad idea to do at random for a remote computer. I’m assuming here that you’ve just plugged in a new Pi right in front of you and you know exactly why you’re getting this error. You should only have to do this once per new Pi.)
You can test that your SSH agent forwarding is working by running
ssh -T git@github.com
again while SSH’d into your Pi.
3.2 - Debugging: Real-Time Clocks (RTC)
A real-time clock (RTC) is a small device equipped with a crystal oscillator and a battery that is responsible for keeping track of the current time, even while your computer (or Raspberry Pi) is powered off.
The “Payload Enclosure Divider” includes a PCF8523
RTC and a holder for a CR1220 coin cell battery. The provided user-config
file
should automatically set this up. As long as you provide internet access to
the Pi once, it will automatically synchronize the RTC with a network time server
and everything will just work.
Tell me more about the PCF8523 and how to use it
Adafruit makes a great breakout for the same chip available here.
They also have a great tutorial on setting up various RTCs with the Pi.
My Pi 5 already has an RTC built-in, right?
Yes, there is. Documentation for the built-in RTC is here.
The built-in RTC still requires adding a rechargable lithium manganese battery to the Pi’s J5 connector. Charging of this battery is disabled by default and must be manually enabled.
In theory, you should be able to add the official Pi RTC battery, follow the official instructions to enable charging, and everything should work.
I have a Pi 5 but still want to use
3.3 - Customizing user-data
The default user-data we start from is as shown below. You will likely need to tweak many of these settings. Descriptions and tips for the most important sections are below.
Note that this is one of two key configuration files. You can read about network-config here.
Starting point user-data file
#cloud-config
# This is the user-data configuration file for cloud-init.
# The cloud-init documentation has more details:
#
# https://cloudinit.readthedocs.io/
system_info:
default_user:
name: ubuntu # Allow the default user to shutdown or reboot the system without entering a password (used by our automated scripts)
sudo: "ALL=(ALL) NOPASSWD: /sbin/poweroff, /sbin/reboot, /sbin/shutdown"
# On first boot, set the (default) ubuntu user's password to "cryosphere"
chpasswd:
expire: false
list:
- ubuntu:$6$rounds=4096$aQ7tu0.beL3WAL32$fKxKYvZpY7EMCoxAU1heRomA3v8WvgbqBhhz08QwOtQdlP/DJOP2BThqZFoRW8d2a9PaIKK9BC9NHs1qNnkya1
# Enable password authentication with the SSH daemon
ssh_pwauth: true
# Set a default timezone
timezone: Etc/UTC
## On first boot, use ssh-import-id to give the specific users SSH access to
## the default user
ssh_import_id:
- gh:thomasteisberg
- gh:albroome
- gh:dfxmay
## Update apt database and upgrade packages on first boot
package_update: true
package_upgrade: true
## Install additional packages on first boot
packages:
- net-tools
- git
- cmake
- g++
- mosh
- exfat-fuse
- i2c-tools
- rpi.gpio-common
- util-linux-extra
## Write arbitrary files to the file-system
write_files:
- path: /home/ubuntu/initial_setup.sh
content: |
#!/bin/bash
exec > >(tee -a "initial_setup_output.log") 2>&1
# Miniconda Setup
wget --progress=bar:force:noscroll "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-Linux-aarch64.sh" -O $HOME/miniconda.sh
bash $HOME/miniconda.sh -b -p $HOME/miniconda
cd $HOME
source .profile
source miniconda/etc/profile.d/conda.sh
conda init bash
# Setup logger environment
git clone git@github.com:thomasteisberg/uav_radar_logger.git
# Clone uhd_radar repo
git clone git@github.com:radioglaciology/uhd_radar.git
cd uhd_radar
#git checkout thomas/dask # Uncomment if you want to check out a specific branch other than main
conda env create -n uhd -f environment-rpi.yaml
conda activate uhd
python /home/ubuntu/miniconda/envs/uhd/lib/uhd/utils/uhd_images_downloader.py
systemctl --user enable radar.service
systemctl --user enable logger.service
ifconfig
sudo reboot
append: true
- path: /home/ubuntu/.profile
content: |
PATH=/home/ubuntu/miniconda/bin:$PATH
source /home/ubuntu/.bashrc
append: true
- path: /home/ubuntu/.ssh/known_hosts
content: |
github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=
- path: /etc/security/limits.conf # Recommended by Ettus https://kb.ettus.com/USRP_Host_Performance_Tuning_Tips_and_Tricks
content: |
ubuntu - rtprio 99
append: true
- path: /etc/systemd/user/radar.service
content: |
[Unit]
Description=Service to run the radar code on startup
[Service]
Type=simple
WorkingDirectory=/home/ubuntu/uhd_radar/
ExecStart=/home/ubuntu/uhd_radar/manager/radar_service.sh
Restart=always
RestartSec=10
KillSignal=SIGINT
[Install]
WantedBy=default.target
- path: /etc/systemd/user/logger.service
content: |
[Unit]
Description=Service to log data from I2C sensors and automatically shutdown below a voltage threshold
[Service]
Type=simple
WorkingDirectory=/home/ubuntu/uav_radar_logger/
ExecStart=/home/ubuntu/uav_radar_logger/logger_service.sh
Restart=always
RestartSec=60
KillSignal=SIGINT
[Install]
WantedBy=default.target
# Run arbitrary commands at rc.local like time
# These commands are run with root permissions
# If you want commands run as a normal user, put them in initial_setup.sh (see above)
# which is run as the "ubuntu" user (see below)
runcmd:
- chown -R ubuntu:ubuntu /home/ubuntu
- chmod +x /home/ubuntu/initial_setup.sh
- wget -O /etc/udev/rules.d/uhd-usrp.rules https://raw.githubusercontent.com/EttusResearch/uhd/master/host/utils/uhd-usrp.rules
- usermod -a -G i2c ubuntu
- usermod -a -G dialout ubuntu
- usermod -a -G tty ubuntu
- apt remove -y modemmanager
- systemctl stop serial-getty@ttyS0.service && systemctl disable serial-getty@ttyS0.service
- i2cdetect -y 1
- echo "dtoverlay=i2c-rtc,pcf8523" >> /boot/firmware/config.txt
- loginctl enable-linger ubuntu
- mkdir /media/ssd
- chown ubuntu /media/ssd
- chgrp ubuntu /media/ssd
- echo "/dev/sda2 /media/ssd exfat defaults,nofail,uid=1000,gid=1000 0 2" | tee -a /etc/fstab
Password-less shutdown
system_info:
default_user:
name: ubuntu # Allow the default user to shutdown or reboot the system without entering a password (used by our automated scripts)
sudo: "ALL=(ALL) NOPASSWD: /sbin/poweroff, /sbin/reboot, /sbin/shutdown"
One of the features supported by the uav_radar_logger utility is to automatically
cleanly shutdown the system if the measured battery voltage drops below a
threshold. To facilitate this, the default user must be able to shutdown the
system without needing additional authentication. This gives permission for the
ubuntu
user to call sudo shutdown
or sudo reboot
without entering a password.
Password authentication
# On first boot, set the (default) ubuntu user's password to "cryosphere"
chpasswd:
expire: false
list:
- ubuntu:$6$rounds=4096$aQ7tu0.beL3WAL32$fKxKYvZpY7EMCoxAU1heRomA3v8WvgbqBhhz08QwOtQdlP/DJOP2BThqZFoRW8d2a9PaIKK9BC9NHs1qNnkya1
# Enable password authentication with the SSH daemon
ssh_pwauth: true
This sets up a default password for the ubuntu
user. You should change this to
something else (or disable password authentication completely, if you prefer).
Passwords are stored in a hashed format. You can generate password hashes using this utility:
mkpasswd --method=SHA-512 --rounds=4096
Timezone
# Set a default timezone
timezone: Etc/UTC
You could set this to other time zones (i.e. `America/Los_Angeles"), but really it would make everyone’s life easier if you just set your clock to UTC.
Add SSH keys through GitHub
## On first boot, use ssh-import-id to give the specific users SSH access to
## the default user
ssh_import_id:
- gh:thomasteisberg
- gh:albroome
- gh:dfxmay
You can very conveniently enable key-based authentication for specific GitHub user names. If your username is in here and you have a public key setup with GitHub, this public key will be imported and you will be able to SSH into your Pi with no additional setup. You might want to remove us from your list, though. :)
Files
Arbitrary files can be written to the system with cloud-init. Some of these are important.
initial_setup.sh
- path: /home/ubuntu/initial_setup.sh
content: |
#!/bin/bash
exec > >(tee -a "initial_setup_output.log") 2>&1
# Miniconda Setup
wget --progress=bar:force:noscroll "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-Linux-aarch64.sh" -O $HOME/miniconda.sh
bash $HOME/miniconda.sh -b -p $HOME/miniconda
cd $HOME
source .profile
source miniconda/etc/profile.d/conda.sh
conda init bash
# Setup logger environment
git clone git@github.com:thomasteisberg/uav_radar_logger.git
# Clone uhd_radar repo
git clone git@github.com:radioglaciology/uhd_radar.git
cd uhd_radar
#git checkout thomas/dask # Uncomment if you want to check out a specific branch other than main
conda env create -n uhd -f environment.yaml
conda activate uhd
python /home/ubuntu/miniconda/envs/uhd/lib/uhd/utils/uhd_images_downloader.py
systemctl --user enable radar.service
systemctl --user enable logger.service
ifconfig
sudo reboot
append: true
The initial_setup.sh
script grabs copies of our code and sets up the radar and
logging services. This script is intended to be manually run the first time you
SSH into the system. This enables you to use
SSH agent forwarding
to provide any needed GitHub authentication to get the code.
This is also where you would customize the repositories to check out (if, for example, you’ve forked our code) and where you can pick a branch to automatically checkout.
Radar and Logger services
- path: /etc/systemd/user/radar.service
content: |
[Unit]
Description=Service to run the radar code on startup
[Service]
Type=simple
WorkingDirectory=/home/ubuntu/uhd_radar/
ExecStart=/home/ubuntu/uhd_radar/manager/radar_service.sh
Restart=always
RestartSec=10
KillSignal=SIGINT
[Install]
WantedBy=default.target
- path: /etc/systemd/user/logger.service
content: |
[Unit]
Description=Service to log data from I2C sensors and automatically shutdown below a voltage threshold
[Service]
Type=simple
WorkingDirectory=/home/ubuntu/uav_radar_logger/
ExecStart=/home/ubuntu/uav_radar_logger/logger_service.sh
Restart=always
RestartSec=60
KillSignal=SIGINT
[Install]
WantedBy=default.target
Two systemd services are used to manage everything. One run the radar code in its default button-controlled setup. The other runs basic logging of I2C-connected sensors and handles automatic low-battery shutdown.
Run commands
runcmd:
- chown -R ubuntu:ubuntu /home/ubuntu
- chmod +x /home/ubuntu/initial_setup.sh
- wget -O /etc/udev/rules.d/uhd-usrp.rules https://raw.githubusercontent.com/EttusResearch/uhd/master/host/utils/uhd-usrp.rules
- usermod -a -G i2c ubuntu
- usermod -a -G dialout ubuntu
- usermod -a -G tty ubuntu
- apt remove -y modemmanager
- systemctl stop serial-getty@ttyS0.service && systemctl disable serial-getty@ttyS0.service
- i2cdetect -y 1
- echo "dtoverlay=i2c-rtc,pcf8523" >> /boot/firmware/config.txt
- loginctl enable-linger ubuntu
- mkdir /media/ssd
- echo "/dev/sda2 /media/ssd exfat defaults,nofail,uid=1000,gid=1000 0 2" | tee -a /etc/fstab
Some final setup is done by running arbitrary commands. These are run as the root user.
One aspect of this you may wish to customize are the last two lines, which add settings to automatically mount an ExFAT-formatted SSD plugged into the Pi. This can be (optionally) used as a storage location for radar data.
Testing changes
You may want to test your changes before using them on your Pi. Options for doing
that are described here.
Note that the initial_setup.sh
script downloads miniconda for the aarch64
architecture, which probably won’t work on your computer. If you want to test that
part, you’ll need to change this.
3.4 - Raspberry Pi Network Connection
Most of the time, the Pi doesn’t need an internet connection. There are, however, a couple of cases where you may want some sort of a network connection to the Pi.
These are:
- Downloading data from the Pi to a computer (requires a network connection but not internet)
- Initial setup with cloud-init (requires internet)
- Updating code by pulling from GitHub (requires internet)
Setting up network interfaces
With cloud-init (first-time setup)
The preferred way to setup network interfaces is by providing them in the
network-config
file read by cloud-init when first setting up the Pi.
An example is shown below:
# This file contains a netplan-compatible configuration which cloud-init will
# apply on first-boot (note: it will *not* update the config after the first
# boot). Please refer to the cloud-init documentation and the netplan reference
# for full details:
#
# https://cloudinit.readthedocs.io/en/latest/topics/network-config.html
# https://cloudinit.readthedocs.io/en/latest/topics/network-config-format-v2.html
# https://netplan.io/reference
version: 2
ethernets:
eth0: # Your ethernet name.
dhcp4: no
addresses: [192.168.11.137/24]
gateway4: 192.168.11.1
nameservers:
addresses: [8.8.8.8,8.8.4.4]
wifis:
renderer: networkd
wlan0:
dhcp4: true
optional: true
access-points:
"<YOUR WIFI SSID HERE>":
password: "<YOUR WIFI PASSWORD HERE>"
The above configuration sets up a static IP over the ethernet interface. It
also configures 192.168.11.1
as the default gateway. This allows for sharing
an internet connection from a computer over this interface if desired.
The configuration also provides an SSID and password for a WiFi network. In practice, we configure this to the settings for a phone hotspot that can be used to get internet when WiFi is not otherwise available. This is also a simpler setup for getting the Pi on the internet when needed.
Reconfiguring with netplan
By default, network interfaces are configured with netplan. See the netplan documentation for more details.
Configuring your computer
If you’re connecting to the Pi via a simple ethernet cable to your computer,
you’ll want both configured with static IPs. The config file above will do this
on the Pi side. On your computer, you’ll want to set a static IP of 192.168.11.1
and a netmask of 255.255.255.0
. For example, your settings may look like this:
Sharing internet from your computer
Do you need to do this?
This process is a little annoying to setup. If you can easily connect your Pi to a separate WiFi network with internet access, you probably don’t need to do this.
If your WiFi network requires complicated configuration to join, requires a key or password you don’t want to leave around on the Pi, or only allows some devices to join, this is an alternative approach that only requires a separate computer (i.e. your laptop) to have internet access.
Sharing your computer’s internet connection from one network interface to another is a useful trick to get an internet connection for the Pi. This can be done between any two network interfaces (i.e. from your WiFi connection to the Pi over ethernet or from one WiFi card to a network created by a second WiFi card).
The Arch Linux wiki has useful instructions for configuring internet sharing on Linux.
To briefly summarize them:
All of this setup will happen on your laptop (or whatever computer has an internet connection).
- Enable IPv4 forwarding:
sudo sysctl net.ipv4.ip_forward=1
- Setup NAT with iptables: (See note below if you have docker installed!)
sudo iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
sudo iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A FORWARD -i net0 -o wlan0 -j ACCEPT
Where wlan0
should be replaced with the name of the network interface on
your computer which is currently connected to the internet, and net0
should
be replaced with the name of the network interface on the same computer which is
connected to the Pi.
Note for Docker users
If you have docker installed on your system, it changes your default firewall settings . As a result, you need to setup NAT slightly differently:
sudo iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
sudo iptables -I DOCKER-USER 1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
sudo iptables -I DOCKER-USER 2 -i net0 -o wlan0 -j ACCEPT