Trans Scend Survival

Trans: Latin prefix implying "across" or "Beyond", often used in gender nonconforming situations – Scend: Archaic word describing a strong "surge" or "wave", originating with 15th century english sailors – Survival: 15th century english compound word describing an existence only worth transcending.

Page 2 of 13

Parse fdisk -l in Python

fdisk -l has got to be one of the more common disk-related commands one might use while fussing about with raw disk images. The fdisk utility is ubiquitous across linux distributions (also brew install gptfdisk and brew cask install gdisk, supposedly). The -l argument provides a quick look raw sector & file system info. Figuring out the Start, End, Sectors, Size, Id, Format of a disk image's contents without needing to mount it and start lurking around is handy, just the sort of thing one might want to do with Python. Lets write a function to get these attributes into a dictionary- here's mine:

import subprocess
import re

def fdisk(image):

    #  `image`, a .img disk image:
    cmd = str('fdisk -l ' + image)

    # read fdisk output- everything `cmd` would otherwise print to your console on stdout
    # is instead piped into `proc`.
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)

    # the raw stuff from stdout is not parseable as is, so we read into a string:
    result =

    # figure out what type we should iterate with when looking via file / part contained within image.  I have no idea if anything besides .img will work- YMMV, but YOLO xD
    if '.iso' in result:
        iter = '.iso'
    if '.qcow2' in result:
        iter = '.qcow2'
        iter = '.img'

    # chop up fdisk results by file / partition-
    # the resulting `parts` are equivalent to fdisk "rows" in the shell
    parts = re.findall(r'' + iter + r'\d', result)

    # dictionary `disk` contains each "row" from `parts`:
    disk = {}
    for p in parts:
        # sub dictionary 'part' contains the handy fdisk output values:
        part = {}
        # get just the number words with regex sauce:
        line = result.split(p)[1]
        words = re.split(r'\s+', line)
        # place each word into 'part':
        part['Start'] = words[1]
        part['End'] = words[2]
        part['Sectors'] = words[3]
        part['Size'] = words[4]
        part['Id'] = words[5]
        part['Format'] = words[6].split('\\n')[0]
        # stick this part into 'disk', move onto next disk part:
        disk[p] = part
    return disk

Dover’s Enclosure

A stylish demo enclosure for the Xilinx / Digilent Genesys 2 with a display panel.

Check out what Dover Microsystems is up to here:

Prototyping & production @ the D&M Makerspace- see what else we're up to:


FPGA- Digilent Xilinx Genesys 2 FPGA Reference

The display and HDMI driver board from pimoroni-
The sketch for panel dimensions are shared over here too

BOM for version 6:

You can find the V6 interactive Fusion 360 model over here

...additional V6 svg, stl layouts on tinkercad


Size Type
12"x12" 1/4" (6.35mm) clear acrylic sheet
12"x12" 3mm clear acrylic sheet
12"x12" 3mm colored acrylic sheet
~45 grams printer plastic (filament or resin)


Qty Size
3 m3x8
3 m3x18
1 m3x20
7 m3 nut
2 m2x14
2 m2x10
4 m2 nut

What is this thing? Some words from founding scientist @gregsgit:

*"We use the FPGA to prototype / emulate a "Soft Core" CPU with and without Dover's IP (logic) called CoreGuard.
An FPGA can simulate (sometimes called "emulate") logical circuits, and is reprogrammable. So you can design circuitry that eventually will be fabricated in silicon, but you can work out bugs and try different designs using the FPGA "fabric".*

*For demos, we synthesize to the Xilinx FPGA: a design for a RISC-V CPU, a simple UART (serial interface), an interface to the on-board DDR memory and flash memory, and a simple video output. We put some software in the on-board flash, then boot a working RISC-V system. We'll show how the software can be attacked, using I/O over the serial port to mimic what would typically take place over a network connection. Next, we show the same SoC (CPU + UART + memory) with CoreGuard logic added in. We run the same software and then show that the same attack is blocked by CoreGuard. We also use the FPGA to emulate the Arm CPU that we are interfacing with for our NXP customer."*

The eBird API & regionCode

get this script and other GIS bits here on github

The Ebird dataset is awesome. While directly handling data as a massive delimited file- as distributed by the eBird people- is cumbersome at best, the ebird api offers a fairly straightforward and efficient alternative for a few choice bits and batches of data.

  • The eBird AWK tool for filtering the actual delimited data can be found over here:


It is worth noting R + auk (or frankly any R centered filtering method) will quickly become limited by the single-threaded approach of R, and how you're managing memory as you iterate. Working and querying the data from a proper database quickly becomes necessary.

Most conveniently, the eBird API already exists- snag an key over here.

...The API package for R is over here:

...There is also a neat Python wrapper over here:
pip3 install ebird-api

Region Codes:

I'm not sure why, but some methods use normal latitude / longitude in decimal degrees while some others use "regionCode", which seems to be some kind of eBird special. Only ever seen this format in ebird data.

For example, recent observations uses regionCode:

# GET Recent observations in a region:

...But nearby recent observations uses latitude / longitude:

# GET Recent nearby observations:

Regardless, lets just write a function to convert decimal degrees to this regionCode thing. Here's mine:

#!/usr/bin/env python3
# provide latitude & longitude, return eBird "regionCode"
Written by Jess Sullivan
available at:
import requests
import json

def get_regioncode(lat, lon):

    # this municipal api is a publicly available, no keys needed afaict
    census_url = str('' +
                     str(lat) +
                     '&lon=' +
                     str(lon) +

    # send out a GET request:
    payload = {}
    get = requests.request("GET", census_url, data=payload)

    # parse the response, all api values are contained in list 'results':
    response = json.loads(get.content)['results'][0]

    # use the last three digits from the in-state fips code as the "subnational 2" identifier:
    fips = response['county_fips']

    # assemble and return the "subnational type 2" code:
    regioncode = 'US-' + response['state_code'] + '-' + fips[2] + fips[3] + fips[4]
    print('formed region code: ' + regioncode)
    return regioncode

Prius, Printers

Add EV only mode button for 2009 Prius-

Pinouts and wiring reference here:

Big shiny EV mode button in the Prius!

Fusion 360 files here:

Files uploaded to thingiverse here:

xposted to prius chat too:

PLA & Carbon Polycarbonate button housings:

While we're at it....

See more notes on D&M 3d Printer stuff on github here:
...and here:

While at a safe distance…

...Playing with Bandlab's Sonar reboot --> morning metal ....Frankly the whole suite (yes, Melodyne, the whole nine yards) is way better than when it was with the late Cakewalk, and its all free now. PSA!

...Unexpected success with
Nylon 680 FDA {3mm @ .8} for some rather delicate parts:

...Yet another improved pi monitoring sketch, currently in production w/ polycarbonate & 1/4"... ...or to quote Mad-eye Moody, "CONSTANT VIGILANCE!" 🙂


Install Adobe Applications on AWS WorkSpaces

By default, the browser based authentication used by Adobe’s Creative Cloud installers will fail on AWS WorkSpace instances. Neither the installer nor Windows provide much in the way of useful error messages- here is how to do it!

Open Server Manager. Under “Local Server”, open the “Internet Explorer Enhanced Security Configuration”- *(mercy!)* - and turn it off.

Good Lord

##### Tada! The sign on handoff from the installer→Browser→ back to installer will now work fine. xD

Convert .heic –> .png

on github here, or just get this script:


Well, following the current course of Apple’s corporate brilliance, iOS now defaults to .heic compression for photos.


Without further delay, let's convert these to png, here from the sanctuary of Bash in ♡Ubuntu Budgie♡.

Libheif is well documented here on Github BTW

# recursively convert .heic to png
# by Jess Sullivan
# permiss:
# sudo chmod u+x
# installs heif-convert via ppa:
# sudo ./
# run as $USER:
# ./

command -v heif-convert >/dev/null || {

  echo >&2 -e "heif-convert not intalled! \nattempting to add ppa....";

  if [[ $EUID -ne 0 ]]; then
     echo "sudo is required to install, aborting."
     exit 1

  add-apt-repository ppa:strukturag/libheif
  apt-get install libheif-examples -y
  apt-get update -y

  exit 0


# default behavior:

for fi in *.heic; do

  echo "converting file: $fi"

  heif-convert $fi $fi.png

 # FWIW, convert to .jpg is faster if png is not required 
 # heif-convert $fi $fi.jpg


D&M Shields – Fusion 360

As of 4/4/20, we are busy 3d printing our rigid shield design, efficiently hacked into its current form by Bret here at D&M. click here to visit or download the Fusion files!

The flat, snap-fit nature of this design can easily be lasercut as well- the varied depths of the printed model are just an effort to minimize excess plastic and print time.

More to come on the laser side of things- in addition to the massive time savings- like <20 seconds vs. >3 hours per shield- we can use far cheaper and varied materials with the addition of our sterilizable and durable UV resins and coatings. Similarly, lasercut stock + resin offers the possibility quick adaptation and derivative design, such as [flexible]( UV cured forms.

ppe & whatnot

Yep, we too are busy cooking up protective medical devices.......


& whatnot:

Prototyping bits, bobs for an ada motorsports startup-

ADA auto prototyping

Fast Pi camera stand sketch:

Quick pass at a low friction filament spool holder for some very fragile materials:

Some GDAL shell macros from R instead of rgdal

also here on github

it's not R sacrilege if nobody knows

Even the little stuff benefits from some organizational scripting, even if it’s just to catalog one’s actions. Here are some examples for common tasks.

Get all the source data into a R-friendly format like csv. ogr2ogr has a nifty option -lco GEOMETRY=AS_WKT (Well-Known-Text) to keep track of spatial data throughout abstractions- we can add the WKT as a cell until it is time to write the data out again.

# define a shapefile conversion to csv from system's shell:
sys_SHP2CSV <- function(shp) {
  csvfile <- paste0(shp, '.csv')
  shpfile <-paste0(shp, '.shp')
  if (!file.exists(csvfile)) {
    # use -lco GEOMETRY to maintain location
    # for reference, shp --> geojson would look like:
    # system('ogr2ogr -f geojson output.geojson input.shp')
    # keeps geometry as WKT:
    cmd <- paste('ogr2ogr -f CSV', csvfile, shpfile, '-lco GEOMETRY=AS_WKT')
    system(cmd)  # executes command
  } else {
    print(paste('output file already exists, please delete', csvfile, 'before converting again'))

Read the new csv into R:

# for file 'foo.shp':
foo_raw <- read.csv(sys_SHP2CSV(shp='foo'), sep = ',')

One might do any number of things now, some here lets snag some columns and rename them:

# rename the subset of data "foo" we want in a data.frame:
foo <- data.frame(foo_raw[1:5])
colnames(foo) <- c('bar', 'eggs', 'ham', 'hello', 'world')

We could do some more careful parsing too, here a semicolon in cell strings can be converted to a comma:

# replace ` ; ` to ` , ` in col "bar":
foo$bar <- gsub(pattern=";", replacement=",", foo$bar)

Do whatever you do for an output directory:

# make a output file directory if you're into that
# my preference is to only keep one set of output files per run
# here, we'd reset the directory before adding any new output files
redir <- function(outdir) {
  if (dir.exists(outdir)) {
    system(paste('rm -rf', outdir))

Of course, once your data is in R there are countless "R things" one could do...

# iterate to fill empty cells with preceding values
for (i in 1:length(foo[,1])) {
  if (nchar(foo$bar[i]) < 1) {
    foo$bar[i] <- foo$bar[i-1]
  # fill incomplete rows with NA values:
  if (nchar(foo$bar[i]) < 1) {
    foo[i,] <- NA  

# remove NA rows if there is nothing better to do:
newfoo <- na.omit(foo)

Even though this is totally adding a level of complexity to what could be a single ogr2ogr command, I've decided it is still worth it- I'd definitely rather keep track of everything I do over forget what I did.... xD

# make some methods to write out various kinds of files via gdal:
to_GEO <- function(target) {
  print(paste('converting', target, 'to geojson .... '))
  system(paste('ogr2ogr -f', " geojson ",  paste0(target, '.geojson'), paste0(target, '.csv')))

to_SHP <- function(target) {
  print(paste('converting ', target, ' to ESRI Shapefile .... '))
  system(paste('ogr2ogr -f', " 'ESRI Shapefile' ",  paste0(target, '.shp'), paste0(target, '.csv')))

# name files:
foo_name <- 'output_foo'

# for table data 'foo', first:
write.csv(foo, paste0(foo_name, '.csv'))

# convert with the above csv:


« Older posts Newer posts »