Friday, July 10, 2020

Review: Infinite Powers: How Calculus Reveals the Secrets of the Universe

You wouldn't think that an audio book on calculus would be a page turner, or whatever the audio equivallent of a page turner is. And yet, I found Steven Strogatz's Infinite Powers: How Calculus Reveals the Secrets of the Universe to precisely this. Stogatz's text is blend of history, math concepts, and novel case studies.

On the history side, Stogatz adds real dimension to characters like Galileo, Newton and other classic names. He manages to both humanize these individuals as well as show them as the mathematic uber geniuses they were. I also learned about new folks like Sofia Kovalevsky, who holds a number of firsts for women in mathmatics.

And then there are the interesting case studies. Strogatz shows how calculus was deployed in topics ranging from GPS development to facial reconstruction surgery. With Covid-19 pandemic as context, I found the case study relating to AIDS drug therapies to be especially fascinating. But <insert name of teacher>, am I ever going to need to use <insert advanced math concept> in the real world? Strogatz makes the case, yes, yes you will.

The part of the text that I truly relished was when Strogatz switched into math-teacher mode. From his demonstration of using infinity as a problem solving tool, to his explanation of euler's number and countless topics in between, I loved how clearly he could break down complex material. I was blown away when he effortlessly explained why we can't divide by 0, something I'd taken as gospel but never considered the underlying reason. Being an audio book, however, some topics went beyond my ability to mentally visualize them.

Consider integration. Prior to listening to Infinite Powers I could have told you that one used integration to find the area under a curve. What I couldn't tell was: (a) how to perform an integration and (b) why finding the area under a curve is so useful. I still can't do (a) but thanks to Strogatz, I can explain (b). And in some respects the why is even more imporant than the how.

If you're still on the fence about Infinite Powers, here's another factor to consider: it makes a powerful sleep aid. Our 5 month old and I listened to this book, and on many occasions, it helped deliver him to dream land. And if the kid grows up to be a mathmetical genius, we'll be able to trace his first exposure to advanced mathematical topics to this book.

I'm telling you, stepping a back into 'math class' with Strogatz is a worthwhile endevor. You'll walk away fresh ideas percolating in your head and no pesky tests or homework to stress you out.

Wednesday, July 08, 2020

That's So Covid

That's us picking blueberries for our 22nd wedding anniversary. In masks. Cause, you know, Covid.

Here's before and after pics from my first haircut during the pandemic. I got my hair cut right before July 4th, thinking there may be a spike in cases due to folks not social distancing during the holiday. Hopefully I'm wrong. Man, that was some special hair.

Thursday, July 02, 2020

A Local Guide to the Hazards of Covid-19

I love the seemingly endless ways to access and visualize Covid-19 data. Making sense of this data, however, is a different matter. That I've yet to crack. One of the best resources I've found to helps make sense of what's going on locally is Dr. Mike Silverman's Friday Night Facebook Update.

Dr. Silverman is the head of Arlington's Virginia Hospital Center's ER. Every Friday for the past couple of months he's been writing a summary of what he's seeing both in terms of local activity as well as national trends. It's become a tradition in our household for Shira to read the latest update aloud on Friday evening.

To get his take on the current status of the virus is to get hyperlocal information from an expert who's on the front lines. It doesn't hurt that he's a solid writer, too.

If you're an Arlingtonian, he's a must follow. If you're not, then you owe it to yourself to search out your own local Dr. Silverman.

My take away from recent updates: wear a mask, keep up the social distancing, don't panic, don't get cocky with the progress we've made and wear a mask.

Tuesday, June 30, 2020

A Programmer's Understanding Of e

Imagine I get a request from a bank to program the function that calculates money earned from one of their accounts. This seems too easy, but here's the code:

(define ($ amt)
  (inexact amt))


(define (grow/1 balance rate)
  (let ((interest (* balance (/ rate 100))))
    ($ (+ balance interest))))

;; Starting balance is $37 and growing by 8%
> (grow/1 37 8)
39.96

;; Confirm an edge case. 0% means no growth.
> (grow/1 37 0)
37.0

;; Another edge case. Growing 100% means doubling your money.
> (grow/1 37 100)
74.0
> 

"Close," the bank replies, "but not quite." The issue: we need to support growing the money in multiple increments. My imaginary contact explains:

Along with promising our customers 8% interest, we also tell them that their money will grow quarterly. So rather than one 8% increase at the end of the year, we give four 2% increases throughout the year.

That's not too tricky to program. Now my growth function takes in an additional frequency argument:

(define (grow/2 balance rate frequency)
  (let ((r (if (or (= 0 rate) (= 0 frequency))  0
               (/ (/ rate frequency) 100))))
    (let loop ((balance balance) 
               (frequency frequency))
      (cond ((= frequency 0) ($ balance))
            (else
             (let* ((interest (* balance r)))
               (loop ($ (+ balance interest))
                     (- frequency 1))))))))
;; The same $37 at 8%, but now grown quarterly
(grow/2 37 8 4)
40.04998992000001

;; The 0% interest edge case still produces no growth. Good.
> (grow/2 37 0 4)
37.0

;; Growing by 100% now *more* than doubles your money. Nice!
> (grow/2 37 100 4)
90.33203125

Growing the money 4 times a year at 8% gives a slightly better return. But check out that 100% edge case: that now more than doubles the money in the account. Looking at these examples one wonders if we can get a better return just by having the bank grow the money more often. Let's try it:

;; Quarterly - good.
> (grow/2 37 100 4)
90.33203125

;; Monthly - better.
> (grow/2 37 100 12)
96.68230573831309

;; Daily - best!
> (grow/2 37 100 365)
100.43899683480936

;; 1000 times per year
> (grow/2 37 100 1000)
100.52618549272809

;; 5000 times per year
> (grow/2 37 100 5000)
100.56637185376789

This strategy of incrementing the balance more often starts off promising. If we can get the bank to increment our money every day for a year we'll make a little over $100, versus the $90 we'll make growing the money quarterly. However, there's a limit to this strategy. Growing the money 1000 or 5000 times doesn't get us past the $100 return.

This makes sense: we may be able to convince the bank to grow our money 5000 times a year instead of 4--which sounds good--but each increase is using an itty bitty (a technical math term) interest rate.

So this is intereting, but what does this have to do with the math constant e.

Check this out: if you look up e you'll see that its value is approximiately:

  e = 2.718281828459045

Now let's say we run our growth/2 function on the simplest possible arguments: $1 grown at 100%. We get:

> (grow/2 1 100 100000)
2.718268237174493

That number is pretty close to e. Coincidence? Nope. An Intuitive Guide To Exponential Functions & e explains:

The number e (2.718…) is the maximum possible result when compounding 100% growth for one time period. Sure, you started out expecting to grow from 1 to 2 (that’s a 100% increase, right?). But with each tiny step forward you create a little dividend that starts growing on its own. When all is said and done, you end up with e (2.718…) at the end of 1 time period, not 2. e is the maximum, what happens when we compound 100% as much as possible.

So e isn't some mysterious number. It's the result of (grow/2 1 100 x) where x is very large number. So large that it's just about infinity. As we suspected, e is a limit:

e is like a speed limit (like c, the speed of light) saying how fast you can possibly grow using a continuous process. You might not always reach the speed limit, but it’s a reference point: you can write every rate of growth in terms of this universal constant.

While it's novel that we can discover this limit ourselves, that's not the whole story.

Back in 2018 I experimented with an animation framework with an odd quirk. You could draw exactly one thing: lines from (0,0) to (0,1). You could then scale, rotate and translate these lines. So while you could only draw one type of line, you could transform their size, shape and position in whatever you imagined. You can play a similar game with e. By raising e to a power, you can scale it to calculate any growth rate. Including negative growth (I'm looking at you radioactive half-life).

As a sign of how frequently one raises e to various powers Scheme names this function simply exp. Here's yet another grow function, this time powered by e.

(define (grow/3 balance rate)
  ($ (* balance (exp (/ rate 100)))))

> (grow/3 37 8)
40.08162150397347
> (grow/3 37 0)
37.0
> (grow/3 37 100)
100.57642765298466

So not only do we have a sense of what e is, we now have a new tool in our tool kit: ex. If you find yourself dealing with problems relating to continuous exponential growth, like say an out of control pandemic, e is your friend.

Thursday, June 25, 2020

But the Car Can't Do That | Lessons From a Mercedes A220 UI Glitch

Shira starts up the car and drives away. A few minutes into her drive she looks down to see this:

That's a Mercedes A220 dashboard minus all the gauges. She wasn't stopped for speeding, but had she been the answer would have been: no officer, I have no idea how fast I was going. That's because she had no speedometer on her display.

The A220's fully digital dashboard is sweet. You can switch up themes to change the look and feel. But alas, because it's software, it can also be buggy. And apparently Shira tripped over just such a bug. On 495. Going 60'ish miles per hour.

At a stop light she cut the engine and started up the vehicle again. There was no change. After leaving the car parked for a few hours, it went back to normal.

I recently had a similar, though far less dramatic experience. My ASUS C302 Chromebook wouldn't charge. There were only three explanations I could imagine: the outlet was bad, the cable was bad or the computer was bad. I tried another outlet and found it wasn't at fault. I ordered a new cable from Amazon and found it wasn't at fault. That meant that the charging port on the computer was busted. Or was it?

The first hit of a Google Search turned up an alternative explanation:

Hello all, I got an Asus chromebook flip about a month ago and today I plugged it in to charge and I got no response from the computer or the little indicator on the side.
...
This happened to me even though I was using the stable channel. I found another post on here about holding Refresh while hitting the Power button to perform an EC Reset (Reset charging controller).

Sure enough, holding down refresh and the hitting the power button fixed the problem. Apparently the software handles charging the device ran into an issue.

So is software an awful replacement for hardware and we're all doomed? Uh, no. As a programmer I'm hardly ready to give up on the terrific power and flexibility that software introduces into a system. But also as a programmer, I think it's important to stay humble.

When topics like electronic voting come up it's worth remembering examples like the above. "It'll be foolproof" they tell us. At which point I'll kindly chime in with "let me tell you about the time my wife found herself speeding down the highway without a clue as to her speed."

It boils down to this: Software is awesome. Proceed with caution.

Friday, June 19, 2020

An Ultra-Lightweight PHP Unit Test Framework

A project of mine called for a major DB re-org. The result was going to correct a number of longstanding system issues. It was also going to break the entire application. In theory, once the library code that powers the app was updated to work with the new database structure, the software would be fixed. To insure I addressed all the issues I decided to write a suite of unit tests and watch as they slowly went from all failing to all succeeding.

Given that the application is written in PHP, the obvious choice for a unit test framework was PHPUnit. After reading the docs, however, I couldn't resist putting together my own framework. It had three guiding principles.

First, I wanted tests to be easy to write. The convention I arrived was to have a collection of files nested under a top level tests directory. Each file would contain a variable named $tests that was an array of functions. Each function represents a single test. If the function executes without throwing an exception it is considered passing. That description sounds complex; in pracice the code is pithy:

 $ cd tests/utils/date-and-time
 $ cat time.php
 <?php
   $tests = [
     function() {
       $t1 = strtotime("tomorrow 3:00pm");
       $t2 = strottime("+1 day 3:00pm");
       assert($t1 == $t2);
     },
     
     function() {
       $t1 = time() + (60*60*24*3) + (60*60*3);
       $t2 = strototime("+3 days 3am");
       assert($t1 == $t2);
     }
   ];
 ?>

assert is a standard php function and can be configured to throw an exception. Though any code that throws an exception can be used to fail a test. By catching exceptions, it's possible to confirm that negative paths are working too. For example:

  $tests = [
    function() {
      $name = gen_unique_username();
      $u1 = new_user(['username' => $name]);
      try {
        $u2 = new_user(['username' => $name]);
        assert(false, "Uh, should have failed with duplicate user ex");
      } catch(DuplicateUserException $ex) {
        assert(true, "Hurray, we got a dup user ex");
      }
    }
  ];

Beyond picking the path for the .php file, nothing else is named. That's by design, as it makes composing tests that much simpler.

The next principle I was after was to make the test suite easy to run. Because the framework is so lightweight, it's straightforward to create top level programs that run the tests. For example, here's a test driver that runs from the command line:

<?php
require_once(__DIR__ . '/../lib/siteconfig.php');
(php_sapi_name() == 'cli') || die("I'm a command line tool, thanks.");


function show_outcome($file, $index, $error = false) {
  $path = preg_replace('|^.*?/tests/|', '', $file);
  echo "$path:$index:" . ($error ? $error : "pass") . "\n";
}
$stats = testing_run_all(['fail' => 'show_outcome']);

  
echo "\nPass: {$stats['pass']}, Fail: {$stats['fail']}\n";

And here's a similar version that runs from within the web app itself:

<?php
require_once(__DIR__ . '/../lib/siteconfig.php');
header("Content-Type: text/plain");
(g($_GET,'key') == 'a8a49399f8073e7f26d87a12771b954f') || die("Access Denied");

function show_outcome($file, $index, $error = false) {
  $path = preg_replace('|^.*?/tests/|', '', $file);
  echo "$path:$index:" . ($error ? $error : "pass") . "\n";
}
$stats = testing_run_all(['fail' => 'show_outcome']);

  
echo "\nPass: {$stats['pass']}, Fail: {$stats['fail']}\n";

And here's a version that's intended to run from cron and doesn't output the details of the tests. Instead, it logs the outcomes to AWS's CloudWatch, which make the data trivial to include in a system-wide dashboard.

<?php
require_once(__DIR__ . '/../lib/siteconfig.php');

(php_sapi_name() == 'cli') || die("I'm a command line tool, thanks.");

function show_outcome($file, $index, $error = false) {
  $cloudwatch = get_cloudwatch_instance();
  $cloudwatch->putMetricData([
    'Namespace' => 'app-unit-tests',
    'MetricData' => [
       [ 'MetricName' => $error ? "fail" : "pass",
         'Timestamp'  => time(),
         'Value'      => 1,
         'Unit'       => 'Count' ]
     ]
  ]);
}
testing_run_all(['fail' => 'show_outcome']);

Finally, I wanted make the framework easy to embed into other projects. By keeping the framework lean, I was able to hit this goal. Recently I got to test this out by packaging unit tests with a custom WordPress plugin. The tests directory was out of the way, and a WordPress friendly test running program was easy to write. Here's the test runner's code. Note how I'm using the prefix 'foo', it assumes I'm authoring with 'foo' plugin.

<?p<?php
/*
 * A PHP file for implementing a trivial unit test driver
 */
require_once(__DIR__ . "/../../../../wp-config.php");
header("Cache-Control: no-cache");
header("Expires: 0");
(foo_g($_GET, 'key') == '81b05f7ad603f5d7ae925e44d08ae14b') || wp_die("Permission Denied");
?>
<html>
  <head>
    <style>
     table {
       border-collapse: collapse;
       border: 1px solid #222;
       width: 100%;
     }
     table td {
       border-top: 1px solid #666;
       vertical-align: top;
     }
     td, th {
       padding: 1em;
     }
    </style>

  </head>
  <body>
    <table>
      <tr>
        <th>Test</th>
        <th>Outcome</th>
      </tr>
      <? $stats = foo_testing_run_all(['fail' => 'show_outcome', 'pass' => 'show_outcome']); ?>
    </table>

    <h2>Outcomes</h2>
    <p>
      <b>Pass:</b> <?= $stats['pass'] ?>
    </p>

    <p>
      <b>Fail:</b> <?= $stats['fail'] ?>
    </p>

  </body>
</html>

<?
function show_outcome($file, $index, $error = false) {
  $path = preg_replace('|^.*?/tests/|', '', $file);
  echo "<tr>";
  echo "<td>$path:$index</td>";
  echo "<td><pre>" . ($error ? $error : "pass") . "</pre></td>";
  echo "</tr>";
}

Below is the code for the test framework. Feel free to grab and use it. Remember: tests get easier and more addictive the more you write. Like, say, version control once you've setup your environment and overcome the learning curve you'll wonder how you ever lived without this tool.

<?php
// shared/lib/testing.php -- test framework
function testing_run_all($options = []) {
  return directory_deep_fold(__DIR__ . '/../../tests',
                      function($file, $carry) use($options) {
                        $stats = testing_run($file, $options);
                        return [
                          'pass' => $stats['pass'] + $carry['pass'],
                          'fail' => $stats['fail'] + $carry['fail'],
                        ];
                      }, ['pass' => 0, 'fail' => 0]);
}

function testing_run($file, $options) {
  set_error_handler('exception_error_handler');
  ini_set('zend.assertions', true);
  ini_set('assert.exception', true);

  $pass_handler = g($options, 'pass', function($file, $index) {});
  $fail_handler = g($options, 'fail', function($file, $index, $cause) {});
  require_once($file);
  $stats = ['pass' => 0, 'fail' => 0];
  $ctx = [];
  if(isset($tests)) {
    foreach($tests as $i => $t) {
      try {
        $ctx = $t($ctx);
        $pass_handler($file, $i);
        $stats['pass']++;
      } catch(Throwable $ex) {
        $stats['fail']++;
        $fail_handler($file, $i, $ex);
      } catch(Exception $ex) {
        $stats['fail']++;
        $fail_handler($file, $i, $ex);
      }
    }
  } else {
    $fail_handler($file, 0, new Exception("No variables \$tests defined"));
    $stats['fail']++;
  }
  return $stats;
}

// Utility Functions
function g($array, $key, $default = false) {
  return array_key_exists($key, $array) ? $array[$key] : $default;
}

function directory_deep_fold($root, $fn, $carry) {
  $dh = opendir($root);
  while($file = readdir($dh)) {
    if($file == '.' || $file == '..') {
      continue;
    } else if(is_dir("$root/$file")) {
      $carry = directory_deep_fold("$root/$file", $fn, $carry);
    } else {
      $carry = call_func($fn, "$root/$file", $carry);
    }
  }
  return $carry;
}

// Borrowed from: https://www.php.net/errorexception
function exception_error_handler($severity, $message, $file, $line) {
  if (!(error_reporting() & $severity)) {
    return;
  }
  throw new ErrorException($message, 0, $severity, $file, $line);
}

Thursday, June 11, 2020

The Magic of Plant Snap and The Taste of Honeysuckle Tea

The other day, while strolling through our neighborhood, I came across large swath of greenery dotted with delightful flowers:



My first instinct was to snap pics. Which I did. A lot. Then I turned to new app I've been playing with: Plant Snap. Plant Snap identifies plants from a picture you take within the app.

The concept reminds me of this classic commic:

To my shock, the app works surprisingly well. I tried it on a number of known plants and it properly identified most of them.  On unknown plants, it has served to give me a solid starting point. The app isn't magic and I wouldn't trust it blindly. But as a tool for the amateur botanist, especially one who doesn't want to nag others to learn about plants, the app appears to be a winner.

At $9.99/year, Plant Snap isn't cheap. But it has one notable feature which may justify the price: if automatic plant ID fails you can supposedly get a knowledgeable human to step in and help.

Back in front of the pretty flowers I snapped a pic with PlantSnap:

I was looking at Japenese Honeysuckle. Follow up research showed this to be true. I'm still amazed that the app guessed this from a single picture of a leaf.

Reading up on honeysuckle, it's a wonder  I didn't instantly recognize the plant myself. It's one of those classic plants that was brought to the US with such promise, and then proceeded to wreak utter havoc. While the plant is medicinally promising, pretty and easy to grow, it's fast growth and habit of strangling other plants means it's often considered an invasive weed.

Honeysuckle smells delightful, and a quick Google search revealed that you can make tea from the flowers. Which naturally I had to do. The stand of honeysuckle I found was on public land and plentiful, so I had no qualms about picking a few flowers to give this tea a try.

Preparation was simple: I put the flowers in a small glass jar, added boiling water and waited. I ran the drink through a strainer and found myself with a warm cup of yellow-green tinted liquid. I took a sip: it tasted flowery, woody and leafy. It wasn't love a first sip, but it wasn't bad either. On a backpacking trip, where everything tastes better, I bet having some fresh honeysuckle tea would be a nice treat.



I've always tried to treat my local environment as a sort of treasure hunting grounds: the gems are there, you just need to look past the see-it-everyday blindess that sets in. Plant Snap is the perfect tool for embracing this philosophy. Every random green thing in your neighborhood is now a click away from revealing its identity. So get snapping.

Friday, June 05, 2020

A Most Unusual Bedtime Playlist

Picking music for your baby's bedtime is easy. Fire up Soma FM's Groove Salad and you've got the perfect environment to send your little one off to dream land.

Except our little guy didn't get the memo. When he gets overtired and both desperately needs and fights sleep the ambient beats over at Groove Salad do nothing. Classical Music, Chill and Post Rock all come up equally short.

What he needs, and it's beyond counterinutive, is to rock out to some bang'n tunes. I wouldn't believe it except I witness this on a daily basis. To watch our little one drift off to sleep while Dwight Yokum or Bishop Briggs loudly jams away is to witness a mystery and miracle.

So here it is, the world's most unusual bedtime music list. Kids are weird.

Sweet Dreams!

Monday, June 01, 2020

From Ferocious Meat-Eater to Handsome Acrobat

Meet Ed the Eyed Elater. Ed is a handsome beetle with a passion for acrobatics (he can flip himself off his back, hurling himself four times his body length). But don't let Ed's good looks and clever party trick fool you. In larvae form, he's a beast:

Found under logs and other dark, damp places, the Alaus oculatus larva looks like a stocky, yellowish-brown, segmented worm. It has a flat, dark brown rectangular head that ends in 2 powerful jaws. The jaws, which resemble small crab legs, are used to disable and dismember prey. An individual is about 2 inches long. It looks rather dangerous at the posterior end, too. The 10th segment has 2 anal hooks, 10-12 spines, and setae (hairs) in front of the anus.

Fortunately, Ed puts his powerful jaws, anal hooks and spines to good use eating garden pests. So if you see Ed, a Thank You For Your Service is in order.

Mostly I'm glad Ed stopped by my porch so I could meet a new critter all without leaving the comfort of our quarantine.

Wednesday, May 27, 2020

Skills Inspiration from a Real Life Worst Case Survival Guide

I can't explain it: whenever I pick up big 'ol book on wilderness skills or survival I feel buzz of excitement. Perhaps it's the fixer in me that is eager to synthesize and deploy this new found knowledge. Or maybe I got a kick out of reading these types of book as a kid and this is just a burst of nostalgia. Whatever it is, I love me a survival manual.

Mind you, these texts are often limited in value. The textual descriptions, vague line art, and random pictures often leave a lot to be desired. Ultimately, there's no substitute for experience; even if that experience is in your backyard. Still, these manuals do serve one notable purpose: they offer inspiration for which skills one should investigate and master.

Kicking around on the web, I came across one of these references I hadn't seen before. AF Handbook 10-644 - Survival Evasion Resistance Escape (SERE). This is apparently the Air Force's manual for their SERE program; the course pilots takes to be prepared for ejecting over enemy territory.


The SERE course is legendary stuff and covers everything from basic wilderness survival to evading the enemy to dealing with be a prisoner of war. To even get a glimpse into this world is a treat.

The range of topics covered is impressive. From nuts and bolts of survival to higher level topics such as understanding weather and various types of insulation; from surviving an avalanche to making landfall in raft; the book tries to cover it all. 

Sprinkled throughout the text are accounts of soldiers who deployed  the documented techniques. Consider this tidbit from page 621.

Colonels John Dramesi, a USAF F-105 pilot shot down in April 1967, and Edwin Atterberry, a USAF RF-4C pilot shot down in August 1967, used a disguise as the main focus of their escape attempt from the “Hanoi Hilton”. They used a combination of ground iodine pills and redbrick dust to match the average skin color of the North Vietnamese. Sandals modeled after the shoes of the North Vietnamese peasant. They gathered bits of cloth and string and made white “surgical” masks to disguise facial features. Using thread pulled from towels and needles made of copper wire, they fixed their black prison clothes to look like peasant dress. Out of strips of rice-straw pulled from sleeping mats, the IP wove two conical hats. Originally they had camouflage nets made from three blankets with clumps of rice-straw from brooms sew on them, but were forced to turn them over to the rest of their cell mates, so they used mosquito netting with clumps woven into them. The IP also stole a burlap bag, two baskets, and a carrying pole as props to look like traveling peasants. The Colonels moved through the populated area of Cu Luc North Vietnam without raising suspicion, coming within a yard of policemen and others during their disguised evasion movement. They greeted those that walked with simple nods, while any locals who attempted to talk to them were ignored as the pair cong forward at a constant rate. While their disguised allowed them to get out Hanoi, unfortunately they did not travel far enough before going to a hold-up site and were subsequently captured.

The manual delivers mightily on the inspiration front. Below are 20 skills that I logged as worthy of future investigation and I'm sure I could find more.

If you're looking for a worst case survival guide, AF Handbook 10-644 is where it's at. Master the skills in there and you'll be ready for anything.

  1. Identifying and using 12 common medicinal plants. (page 87)
  2. Crafting an improvised sleeping bag. (page 199)
  3. Crafting improvised footwear, specifically the curiously named Hudson Bay Duffel Bag. (page 192)
  4. Using quartz or pyrite as a spark generator. (page 229)
  5. Using clouds for weather prediction. (page 101)
  6. Using sandstone and other found rocks as an improvised knife sharpener. (page 241)
  7. Learning two common hand sewing stitches. (page 253)
  8. Using the SODIS method of water purification. (page 293)
  9. Understanding improvised methods for determining your current latitude and longitude. (page 410)
  10. Navigating at night using stars. (page 415)
  11. Using an improvised trail marking scheme. (page 430)
  12. Crafting a field expedient oven for baking. (page 365)
  13. Using field expedient methods for preserving food. (page 367)
  14. Constructing an improvised raft. (page 500)
  15. Learning the evasion checklist. (page 530)
  16. Urban navigation: identifying navigational clues from landmarks and local history. (page 623)
  17. Urban navigation: determine direction from weather effects on buildings and infrastructure. (page 625)
  18. Preparing a personal survival kit. (page 278)
  19. Constructing an improvised backpack. (page 450)
  20. Learning basic rock climbing technique and holds. (page 462)

Friday, May 22, 2020

Hiker in Training: Ellanor C Lawrence Park

I'm on a mission to get our 16 week old ready to tackle serious hikes. We've done the prep work and knocked out a short, but nearby trail. Next up was to pick a trail that was longer in distance and drive time.

Our go-to app for finding trails is All Trails. When Shira and I are shopping around for an adventure, we set the difficulty to medium or hard, and the distance to 7+ miles. This time, we looked around for easy trails that would be less than 5 miles. My gosh, there are so many options we'd never heard of!

The one we picked for this adventure was the loop at Ellanor C. Lawrence Park. This 3.8 mile hike is located in a park surrounded by suburbs. Yet, the vibe was that of a real walk in the woods. We caught glimpses of houses only a couple of times and the road noise was minimal.

The trail itself was in great condition and we zipped right through it, stopping only briefly for a snack.

In the age of COVID-19, we wanted solitude not only to enhance the hike but to avoid social distancing issues. At the entrance to the park there's a delightful pond which a few families were congregating around. We slipped by them and on to the trail and from there saw almost nobody else for the rest of the hike.

All in all, our little hiker did great. He endured the 35 minute car ride without an issue and enjoyed the walk in the woods. For our part, we enjoyed discovering a new bit of green space that's perfect for when we need an easy hike. And I even got to snap quiet a few pics of flowers. Ellanor C. Lawrence Park is a winner.

Hiker in Training: Gulf Branch Trail

To recap: I want to take our 16 week old for some serious hiking, but I realize we need to ease into this to avoid total parent and baby meltdown. Phase 1 was to figure out which carrier worked best for hiking. With that done, it was time to pick the first hike.

I wanted a 'real' trail to hike on, but I also wanted it a short drive away so that it was a minimal investment in time and it had be relatively short. Our solution: hike the Gulf Branch Trail which follows Gulf Branch. All told, we did 2 miles, which included 1.5 miles out and back on the Gulf Branch Trail and then another .5 miles of exploring because the little man was sleeping and we could get away with it.

Like Windy Run and other nearby offshoots of the Potomac Heritage Trail, the trail follows a stream down to the Potomac. Even though you're in the midst of Arlington, you feel like you're doing authentic backcountry hiking. We saw a couple of families along the way, but the trail really wasn't busy at all. Best of all, our little guy enjoyed the hike. He got to stare up at trees and have a little picnic at our turn around point.

If you're in Arlington, you're no doubt itching to get out and have an adventure during the COVID-19 lock down. Hiking the Gulf Branch Trail (and the neighboring runs) are an absolute no-brainer.

Hiker in Training: Pre Hikes

My strategy for planning an outdoor adventure could be summed up in two words: Overdo It. You know, jump in the car and drive 3 hours away, into awful weather, to hike a technically difficult trail that's not fully mapped out. Shira puts up with overdoing it, but our 16 week old, not so much. Heck, given how little we've spent in the car, we're not sure he could endure a long'ish car ride, not to mention a long'ish hike.

So I've been trying to be smart about this, and build up our little hiker's ability step by step.

Phase one has been through a series of walks in the neighborhood. The primary goal: figure out which carrier is most comfortable for him and us. The Moby carrier, while great in the winter and for general snuggling, has shined less once the sun is out. Not to mention, adjusting it on the fly is a real chore.

The Baby Bjorn is a sweet carrier, but our little one isn't so little any more, and we've seemed to have hit its maximum carrying capacity. I mean, I'm sure it technically carries heavier children, but I'm not sure how you do this and breathe at the same time.

Running low on options, I busted out the oddly named Fresh Shine Baby Hip Carrier and gave it go. I bought it 3 years ago when we had an especially zoftig baby placed with us. I thought surely it would be too big for our our little guy. In fact, he fit perfectly and the carrier has been a total winner.

The Fresh Shine's design has a seat to hold the baby, which puts nearly the full weight of the baby on your hips. This is the same strategy that's been employed by back-country backpacks for years and it works. The result is no back pain and a carrier that feels agile to move in. It's also lightweight and easy to take the baby in and out of. Though doing this is a two person job. We've had success carrying our little guy facing out and in, which gives us the option have him watch the trail or nap.

With the carrier figured out, it was time to plan a hike.

Friday, May 15, 2020

Giving Ionic's Capacitor a Dev Friendly Boost

I recently started a Capacitor based Ionic project. After years of working with Cordova, the framework initally felt jarring. However, with even a bit of use, I'm already buying into Capacitor's benefits. The Cordova approach of generating Android and iOS projects often worked great, but at times the approach showed itself to be quite fragile. Something would change in the iOS world and you'd be at the mercy of Cordova to update the build process to catch up with this. Not so with Capacitor, which embraces the notion of working directly on Android and iOS native projects.

I do however miss a number of nicities that Cordova offered. Take versioning. It seems obvious that there would be a central location where your project's version number was set and the iOS and Android apps pulled from this. And yet, this isn't the case with Capacitor. The thought of having to launch two different IDE's to manually change the app's version number seems like a recipe for disaster.

Speaking of launching IDE's, the thought of giving up command line building and running of apps is another step backwards.

Fortunately, with a bit of shell scripting the versionining and building challenge for Android can be addressed. Building from the command line on iOS is possible but it will take a bit more research befor I get it figured out. I added this functionality to my ionicassist command line tool. It's now possible to say:

  ionicassist android-cap
  ionicassist ios-cap

To push the version number found in capacitor.config.json to both Android and iOS and build and install the Android project from the command line. The source code for ionicassist is below. Most of the code is straightfoward, though dealing with the ridiculous Info.plist format took some work. xmlstarlet and jq both proved invaluable for this little project. 

#!/bin/bash

##
## Scrip to help with ionic dev
##

action=$1 ; shift
case $(uname) in 
  Darwin)
    PLATFORM_TOOLS=$HOME/Library/Android/sdk/platform-tools/
    APK_DEST="$HOME/Google Drive (ben@ideas2executables.com)/Clients/Uchi/APKs/"
    ;;
  
  CYGWIN_NT-10.0)
    PLATFORM_TOOLS=/d/tools/android/platform-tools/
    APK_DEST="/d/Google_Drive_i2x/Clients/Uchi/APKs/"
    ;;
esac

ADB=$PLATFORM_TOOLS/adb

case "$action" in
  grab-apk)
    name=$(xmlstarlet sel -N x=http://www.w3.org/ns/widgets  -t -v '/x:widget/x:name' config.xml | sed 's/[^A-Za-z0-9_-]//g')
    version=$(xmlstarlet sel -N x=http://www.w3.org/ns/widgets  -t -v '/x:widget/@version' config.xml)
    cp -v platforms/android/app/build/outputs/apk/debug/app-debug.apk "$APK_DEST/$name-$version.apk"
    ;;

  grab-release-apk)
    name=$(xmlstarlet sel -N x=http://www.w3.org/ns/widgets  -t -v '/x:widget/x:name' config.xml | sed 's/[^A-Za-z0-9_-]//g')
    version=$(xmlstarlet sel -N x=http://www.w3.org/ns/widgets  -t -v '/x:widget/@version' config.xml)
    cp -v platforms/android/app/build/outputs/apk/release/app-release.apk "$APK_DEST/$name-$version.apk"
    ;;

  screencap)
    if [ -z "$1" ] ; then
      echo "Usage: $(basename $0) screencap {file}"
      exit
    else
      file=$1 ; shift
      $ADB shell screencap /sdcard/screen.png
      $ADB pull /sdcard/screen.png $file
    fi
    ;;


  screenrec)
    if [ -z "$1" -o -z "$2" ] ; then
      echo "Usage: $(basename $0) screencap {file} {duration}"
      exit
    else
      file=$1 ; shift
      duration=$1 ; shift
      $ADB shell screenrecord --verbose --time-limit $duration /sdcard/screen.mp4
      $ADB pull /sdcard/screen.mp4 $file
    fi
    ;;

  adb)
    exec $ADB "$@"
    ;;

  sync-cap)
    cap_conf=capacitor.config.json
    and_conf=android/app/build.gradle
    ios_conf=ios/App/App/Info.plist
    if [ -f "$cap_conf" ] ; then
      version_name=$(cat $cap_conf  | jq -r  .appVersion)
      version_code=$(cat $cap_conf  | jq -r  .appVersionCode)

      if [ -f "$and_conf" ] ; then
        mv $and_conf $and_conf.prev
        sed -e "s/versionCode .*/versionCode $version_code/"  \
            -e "s/versionName .*/versionName \"$version_name\"/" \
            < $and_conf.prev > $and_conf
        rm $and_conf.prev
      else 
        echo "Android: $and_conf not found, skipping"
      fi

      if [ -f "$ios_conf" ] ; then
        mv $ios_conf $ios_conf.prev
        xmlstarlet ed -u '/plist/dict/key[text() = "CFBundleShortVersionString"]/following-sibling::string[1]'  \
                   -v $version_name $ios_conf.prev > $ios_conf
        mv $ios_conf $ios_conf.prev
        xmlstarlet ed -u '/plist/dict/key[text() = "CFBundleVersion"]/following-sibling::string[1]'  \
                   -v $version_name $ios_conf.prev > $ios_conf
        rm $ios_conf.prev
      else 
        echo "iOS: $ios_conf missing, skipping"
      fi

    else 
      echo "$cap_conf not found. Giving up."
      exit
    fi
    ;;

  android-cap)
    cap_conf=capacitor.config.json
    if [ -f $cap_conf ] ; then
      app_pkg=$(cat $cap_conf  | jq -r  .appId)
      ionic cap copy
      ionicassist sync-cap
      cd android ; ./gradlew installDebug
      $ADB shell monkey -p $app_pkg 1
    else 
      echo "$cap_conf not found. Giving up";
    fi
    ;;

  ios-cap)
    ionic cap copy
    ionicassist sync-cap
    ionic cap open  ios
    ;;
  *)
    echo "Usage: $(basename $0) {grab-apk|grab-release-apk|screencap|adb|sync-cap|android-cap}"
    exit 1
    ;;
esac

Friday, May 01, 2020

Review: The Trumpet of the Swan

What feels like a lifetime ago, 10 year old J was visiting us and we had a few moments of downtime. I suggested we have some reading time and grabbed the first book nearby that I hadn't read: Trumpet of the Swan by E. B. White. We took turns reading a few chapters and by the 3rd chapter we were both hooked. J went home and finished reading the book within a week. It took me longer, but I eventually got through it.

--Spoiler Alert--

I'm of two minds about this story. On one hand, it's brilliant and inspiring. The book pulls no punches: Louis is disabled. He can't speak, and for a swan, speaking is nearly everything. Yet, Louis and his family don't let his circumstances dictate the outcome of his life. Through creativity and hard work, Louis accomplishes far more than a typical swan ever would. What a powerful example for a young person who may feel that their differences somehow makes them less.

As a person who thrives on being a fixer, this notion that no personal limitation can't be hacked is pretty much a core belief. So I can't help but applaud the book as it shows example after example of Louis creatively solving his problems.

On the other hand, I can't help but be bothered by how unrealistic the book is. And by unrealistic, I'm not referring to the suggestion that Louis is capable of such high level learning and reasoning. I'm fine with that; it's a story after all. But what I can't forgive is how White allows every undertaking Louis makes to be a success. Louis wants to learn to write, so he goes to school and learns to write. He wants to make money playing his trumpet, so he gets a job at a camp and makes money playing his trumpet.

Shame on White for only giving Louis one challenge to overcome and providing smooth sailing from there. How is that an example any child can learn from? Being a fixer means you're going to fail. A lot. Learning to pick yourself up and try again is what makes the process of self improvement even possible.

Overall, what the book lacks in authentic problem solving, it makes up for with a fun story and clever characters. This is definitely a book you'll want to read and discuss with your kids.

Sunday, April 26, 2020

Cause My Arms Are Tired | A Field Expedient Baby Carrier

Far and away the fanciest furniture in our home belongs to our little one. Yet, his favorite resting spot continues to be our arms. And while his near 15 pounds of baby cuteness doesn't sound like much, he gets heavy! Our goto carriers are the Moby Wrap* and Baby Bjorn, and they both work really well. Yet, I found myself looking for other ways to help lighten the load.

Inspired by an Aden + Anais extra large swaddle blanket (47x47 inches baby!) that my Sister-in-Law and Brother gifted us, and memories of playing with Furoshiki wrapping, I wondered what would happen if I rigged up a sling to carry the kid around.

I laid the blanket out and tied the opposite corners together like so:

At this point, I had a sort of crude bag that could be slung over my shoulder. Carefully, I slid our 10 week old baby into the space and let the blanket take his weight:

The square knot that bore the most of the weight cinched down nicely and showed no signs of slipping. The blanket itself also felt secure. And just like that, I had the world's worst baby carrier. The knot dug into my back and the kid was nowhere near secure enough to go hands free. Yet the hack showed promise. The weight had been shifted away from my arms, and one hand had been freed up. At this age, the Moby Wrap works great as a place for the baby to cuddle and sleep. Using my improvised sling, which is mostly just me holding him, I found he could face out and interact with the world.

Over the last couple of weeks this little setup has been truly helpful. When I need just a little extra mobility or want to give my arms a rest, into the sling the baby can go. I also like that as long as I carry one of these blankets in his baby bag, I've got a backup baby carrier. This would have been handy when we visited a museum and found out that strollers were a no-no.

Soon he'll outgrow the sling, and at that point I should be able to use more carry options with the Moby Wrap (like say hip carry). But in the mean time, I'll take all the help I can get.

Two final points: First, I bet there's an established way to do what I'm doing. Perhaps it's a product you can buy or a well known hack. Either way, I won't be surprised if someone leaves a comment: uh, if you just bought X, you'd get all the benefits you have now.

Second: Shira's not impressed with the sling idea and has her own solution: lifting weights and having actual arm strength. To each their own, I suppose.


*Case in point, I'm composing this blog post as our little one naps in the Moby Wrap on my chest. Man I love this thing!

Wednesday, April 22, 2020

3am Audio Hack - Playing Lullabies and Netflix at the Same Time

Years ago, as we prepared to take on our first Foster Placement, I read this book. One piece of advice from this text has stuck with me:

There are no maintenance tasks with a baby.

That is, while diaper changing or feeding a little one a bottle may feel like work can be done on autopilot, it's worth striving to avoid this. These so called 'maintenance moments' are in fact a opportunities to connect and be present with the one you're caring for.

Over the years, this advice has served me well. But I also have to admit, even I have my limits. When it's 3am and I'm rocking a little one back to sleep for the 4th time, I think I can be forgiven for wanting to unplug from the moment. And that's what my latest hack is all about.

Did you know that on Samsung devices, you can go to: Settings » Sound and Vibration » Separate App Sound to configure your phone such that one app plays audio through the speaker and other apps play through a Bluetooth headset?

Why is this useful and what does this have to do with rocking a baby to sleep at 3am?

Using this setting you can softly be playing lullabies via Amazon Music on your phone's speaker, while binge watching a TV show on Netflix using a pair of Bluetooth headphones.

Just remember: with great power comes great responsibility; So use with care.

The Separate Sounds functionality should also be useful for solving the age old problem of having Google Maps navigation be announced via the phone's speaker, while a podcast plays via Bluetooth audio. Got to love these non-standard, random'ish features manufacturers add to Android OS.

Tuesday, April 14, 2020

Passover Thoughts, Covid-19 Edition

I've been mulling over what it means to celebrate Passover in a world wrestling COVID-19. For one thing, it meant trading a raucous multi-family seder for a quaint (and delightful!) one between Shira, Myself and our 11 week old.

Like the first Passover our ancestors went through, we find ourselves under a stay at home order, with an unseen force lurking outside ready to do harm. Sure, the CDC has called for social distancing, and not smearing lamb's blood on your doorpost, but the message remains the same: stay home, heed the instructions of experts, and stay safe.

I'm also gaining fresh perspective on what it means to have your life dramatically altered seemingly overnight. We look at the Jews leaving Egypt as going from slaves to free men and women. But that's with the benefit of a hefty dose of hindsight. In the moment, you can appreciate how it must have felt like trading one precarious situation for another. That is, to go from a slave to a wandering people unsure where your next drink or meal could be found.

While COVID-19 restrictions didn't go into effect at the stroke of midnight, they have descended on us at an alarming rate. Suddenly, parents are home-schooling teachers; office workers are work-from-home employees, and a massive number of people are simply unemployed. Basic human interactions, like sharing a meal or giving a hug are now off limits. The science tells us this is all for our own good. But like those Israelites heading into the desert and being told how fortunate they are to be free, you can be forgiven for wondering: what the heck did we get ourselves into?!

Here's to enduring the scourge that is COVID-19 with as little hardship as possible, and diligently looking for ways to use it as fuel for good. Like our ancestors, may we be able to look back at these events and see more than just the challenges. And next year in Jerusalem! Or really anywhere, as long as it's together in the same room.

Monday, April 13, 2020

A First Hike in Gunpowder Falls State Park

Two weeks ago, the stars looked to be aligning: the weather was going to be top notch, and our day free. We could finally get in some quality hiking! Because we wanted to practice smart social distancing, we opted for what appeared to be a low traffic hike: Little Gunpowder, Winterfell, Quarry Loop located in Gunpowder Falls State Park.

We arrived at the trail head to find cold and dreary conditions. On the plus side, the cruddy weather appeared to be keeping people away. Off we trekked into the wilderness, opting to take the hike uphill first to help us warm up.

For the first half of the hike we found the seclusion we were looking for. We saw no other hikers, and while the conditions remained cold and damp, they also served to enhance the scenery. With the mist, it felt like were in enchanted woods.

This was our little guy's first official hike and for the first couple of miles he slept right through it.

As we descended the Quarry Trail and re-joined the Little Gunpowder Trail we found ourselves with a choice: go left and log an out-and-back hike which would get us more miles and a chance to see a waterfall. Or go right and close out the loop we'd started. At this point, our 10 week old woke up and urgently asked where his next meal was. It also became clear that it wasn't going to warm up and that the 70+ degree temps we'd anticipated weren't coming. We hadn't expected to hike the full day in 50 degree weather, and weren't properly equipped for the day.

So we opted to close out the loop. We encountered a few families at this point, and I'm sure we looked like quite the sight: our little guy slurping down a bottle while we hustled back to a warm car.

All told we logged only 4.26 miles out of the planned 6.6. I got yet another hard won lesson in the need to closely watch the forecast and plan accordingly. I kick myself, because if I had planned for cold weather, we'd have almost certainly completed the full hike.

Still, I'm counting the hike as a success. We learned about a new state park to explore, our 10 week old got his first taste of the wilderness, and a number of gear choices were spot on. Mainly, I carried a large backpack which had food, our little one's diaper bag and my essentials, and Shira carried the kid and a small fanny pack with her essentials. At the last minute, Shira opted to use the Moby Wrap carrier instead of a Baby Bjorn one. The reasoning: the Moby does especially well in cold conditions. This turned out to be a smart move.

Later in the day Shira noticed this tweet from our trusted weather friends Capital Weather Gang:

And where's Gunpowder Falls State Park? Outside Baltimore. D'oh!

I look forward to getting back to Gunpowder Falls State Park and seeing this elusive waterfall.

Monday, March 30, 2020

From Office Supplies To Ninja Armaments

Times are tough. Toilet paper, eggs and even flour are scare around here. School is canceled and we're effectively sheltering in place. But here's useful tip: if you have two sheets of printer paper and a few minutes of time, you can have a throwing star. You can also have a boomerang. Welcome to COVID-19 arts and crafts time baby!

I consider my origami skills beginner at best, so I was pleased how approachable the throwing stars tutorial is. Another fun project: make a treasure chest using two origami boxes.

LinkWithin

Related Posts with Thumbnails