Wednesday, May 5, 2021

Mendocino Forest Fifty! 50 miler trip report

Apr 17, 2021
Distance: 58 miles
Elevation: 7k feet gain
Duration: 13hr 6min

This was my first 50 mile run. My previous longest was along the Rogue River in Oregon for about 42 miles (October 2020). I ran this solo with two crew supported aid stations and pizza delivery at the finish. I'm over fifty and a slow runner. I have a long distance backpacking history which means I've hiked 12+ hours in a day before. But never this distance -- the running is relatively new. 

The course is inspired by the Mendocino 50k and a section I discovered by going down the wrong trail on a previous run. The Aug 2019 run was supposed to be a 50k but ended up being 65k. Foreshadowing?

The run started at Big River Beach with the first section heading west along the edge of the town of Mendocino, following the the bluffs at the headlands, a short road section, and then heads north into the Russian Gulch state park. My crew (thanks Sophia!) dropped me off just before sunrise (6:45). I had 13 hours of sunlight and the beach and bluffs to myself. 

The goal on this first 4 miles was to go slow. I kept telling myself it was going to be a long day so "pace yourself." To finish in 12 hours, I would have to go just faster than 4mph (15 minute miles). I like to think in min/km ... the course was around 85km and during training and previous runs I knew that 8 min/km was doable for the distance and elevation. That meant around 11 1/2 hours with some bonus time for a few breaks.  

I started out with a long sleeve shirt and beanie. I considered gloves but I knew I would warm up quickly and early morning start meant less wind. The temperatures were going to be in the high fifties on the coast and mostly overcast (warmer inland but not hot). Perfect running weather. 

The second hour consisted of traversing Russian Gulch along Fern Canyon to the waterfall and then up to the boundary trail and the east trailhead where my crew was going to meet me. The transition from roads to trails starts at the end of Woodstock Dr when you enter Russian Gulch SP. There's a trail that runs along the bluffs with views of Keesbury Bays, the Russian Gulch bridge, and back towards Mendocino.  The SP website had the fern canyon trail closed, which would have meant a detour and a longer run along the boundary trail. It looked open (no signs/gates) despite having some obvious stretches being repaired. 

The first people I met were at the waterfall. Taking photos. I ran past and then walked as the trail snaked to the top. The first real climb followed (+400 ft) and then a gradual incline to Road 409. The first 14km was on pace and I was met by my wife and greyhound for a pit stop. I switched to a short sleeve shirt and sunhat. But mostly this resupply was for water and food. The next resupply was 6 hours away and so I was carrying 2L of water, a sandwich, several granola bars, an apple, trailmix, and assorted candy. I ate a peanut butter and jelly tortilla at the car (messy) and topped off with gatoraide. Call it an 8 minute transition.

From there, I ran north on 409 for a short bit before dropping into the Jackson Demonstration Forest. The trailhead has two trails down to Road 600. I dropped 500 feet down on EZ-Out. Many of the trails are mountain bike friendly which means banked corners. The forest transitions to older trees more redwoods. Each section has highlights: this one is about the change to a forest run. At the bottom, I contour over a bit to catch EZN trail up 500 feet to the ridge. 

This section is scheduled for a timber harvest this summer. I discovered this when checking the online maps for closures and the local paper running a story about protesters sitting in the trees. I came across their camp and ground crew making coffee up on near Rd 410.  At the moment, the trees are tagged for thinning. I'll be interested to see how this changes. I'm neither for nor against logging. I hope it is done sustainably and that the trails are preserved.

I startled my first (and only) mountain biker on the next trail section. Neither were expecting anyone else. There is a network of trails that parallel the road on the ridge. I've run them once before and so I felt comfortable picking my path. The ridge has a pretty decent ascent, up another 300ft before a high point where the trails converge and the road splits. I expected some significant walking and this was one of those sections where conserving energy and eating was important.

After a brief run along the dirt forest service road, trail 508 splits off. This is a fairly wide and rolling section that is perfect for some speed (6 min/km). And if you aren't paying much attention, you hit the 4 hour milestone and 30km mark. The fun ends with a mile or so of uphill on a dirt road that intersects with the primary north/south dirt road (Rd 409 Little Lake Road). Another 1.5 miles and you can escape to Hwy 20. Instead, I'm going south towards Observation Hill. This was another planned walking section for me... lunch! And then running the downs and walking the ups as 409 follows the ridgeline.

I encounter my first runner, someone doing a long loop. And a few cars and mountain bikes. I cross over the intersection with the 6?? trail I'm going to run in another hour or two to head back towards Russian Gulch and I start thinking more seriously about time and distance. I'd started to drift a bit behind schedule because the estimates from the map were longer and the big 6 mile loop from Observation Hill was definitely going to take longer than an hour.  I've run this section multiple times and forget how it drags on forever. Then there's the abandoned boat and the clear cut signaling I'm nearing the trailhead and cell service!

I'd considered another aid station here. Water here would be nice. But I figured my timing would be harder to predict. It is around the 35 and 45km points. I text my support crew with an update. I waste valuable batteries with banter. And decided that I should have carried the recharger and thus turn off my phone (no strava today!) And then a fun and fast 900ft descent of Manly Gulch. The trail passes thru the group campground and then up the other side. Eagle's Roost is crazy steep. And goes on longer than I remember. But it does take time to get up 700ft. The ridge trail is when I realized I wasn't going to make my target because the distance was growing beyond the 50 mile target. But I knew I would be able to finish, I felt strong and focused. 

At Big Tree, the trail leaves the old roadway and descends 600ft of gentle forest trail cutting a nice contour down towards Big River. And my first day hikers! The Mendocino Woodlands day use parking area is close. I reach the bottom, hoping for an outhouse and instead get sunshine and a bench. I take my shoes off for the first time in 6 hours and officially stop trying to go the next 10-12 miles in 2 hours, accepting 4mph. I eat my second lunch and prepare for the last big hill... 3 miles and 900ft gain up Forest History Trail to Observation Hill. 

I run when I can but power hike everything but the mostly flat. The last bit, I slow down to let a group of mountain bikers push their rides up the last stretch. In reality, I was tired and my legs were showing the wear of  nearly 50km. I debated taking the new trail that connects back towards the pit stop (4+ miles instead of the 8 planned), but instead I texted to my crew saying I was still 2 hours out (instead of 1hr). There's some fun running ahead and I didn't want to potentially need to add some miles at the end to make sure I hit 50. So off I went, just thinking about the next 8 mile block... rolling along the 409 and then a sweet fast downhill before a mostly flat grind above Caspar Creek, followed by a crazy hill, and then East trailhead of Russian Gulch for resupplies!

Sounds easy enough. Walk/run on the 409 while eating and recovering from the big climb and preparing for the downhill. The 610 trail is a converted road with a perfect down. Too fast but nothing you can do! A mtn bike passed me. At the switchback, I walked a bit. Then ran some more... until a stumbled on rock. It is a good reminder to rest. So I did. And hurried on because I remember this bit going on longer than it should when I ran a training run.  One of the last running podcasts I listened to before the run was "science of ultra" that talked about "suffering being the state of wanting to be in another state." I embraced this and enjoyed the trail, the trees, the window, the sap stuck to my running shorts, the empty water bag, the boring nature of trail mix, and remembering that smiling makes you 10% faster.

When the trail hits the logging road, I know I'm almost to the canyon floor. I eat a honey stick and force myself to try another granola bar. But then I go back to shuffling along because I'm nearly back to civilization. Well, 500 feet climb up Purple Skirt first. The lower part of that trail is too steep to run. Nope... the entire bit was walked until Rd 409 was in nearly in view, and then the calm and happiness overflowed again. And before long, I was at the car and new snacks! 66km and 9 hours. Thanks Sophia for being my support crew. 

I ate some chips and cookies. I ate some carrots and refilled my water. I took the battery pack for my phone and the headphones. I knew the stretch along the river was 10 miles, and 3-5 miles to get back to Mendocino Woodlands. I was hoping for the shorter more direct route this time. The first time I ran this section I zigzagged and explored. Today I'd have around 4 hours of light. I figured I'd need at least 3 hours and so only stayed around 12 minutes before waving to my wife and dog. She offered to drive me down the road because I only need 10 miles, not 13. Nope.

I turned on some music and shuffled along. The network of trails spreads out but I was trying to follow the specific and shortest path. The bikers love a hill and steep descent. And this section has all of that. Parallel Action was empty of people and I got to escape into my headphones, shuffling along. Walking any hills. Calculating time and distance remaining. Optimistic.  And then over the road and cutting off a section. This goes down, then up, then down, then up. Over the road to "Endo". This has a steep up and slalom like down bit. And then finding Oh No! for more downhill. I switch to glasses from sunglasses here. New trail though. Thankful I chose to run down not up, forgetting about the Forest History climb earlier which was the alternate.

And without much worry besides the time and tiring feet (no injuries or pain), I hit the the junction for Boiler/Fury III and the last downhill. You can watch the trees coming up at your from the canyon floor to anticipate the bottom. The trail runs into a swamp and then follows it for a bit before coming to a fireplace and Mendo Woodlands day use area. From the highpoint on Endo, probably 800 feet descent. But closer to 6 miles from the car. I knew better than to lose my concentration here though as the path isn't obvious. And then the Big River appears and the footpaths merge into the trail. Somewhere here I thought about adding 10 km (6 miles) to get to 100k. The internal voices all voted for no more than required.

Only 10 miles to go! I told myself I wouldn't look at my GPS hoping I was closer. I did the math and would let the fitbit count down the miles. The trail along the river is mostly flat. It starts with lots of bridges, and so my mini mental game was to run to the bridge when I saw one and walk across it and a bit on the other side. I felt good and tried to run. But after 45+ miles, my body and mind would find ways to get me to walk... do I have to pee? stop. Nope.  Is that the fitbit shaking after another kilometer? Nope... 25%. Run some more.  Shuffle some more.  Walk some.  Go away. My pace wasn't too far off the 8min/km goal. It was creeping higher though.  At some point, you realize the best strategy is to run though. Faster than crawling.

I hit the 81km point before 12 hours. And then took a 5 minute break sitting in the trail. Another 20 minutes, and I was looking at my GPS to confirm my location. Yep. Still there. Nope, no cellular service so I can't call for rescue. No one else was out, or I might have asked for a ride on their bike. And not suffering, because I still wanted to be there. I still wanted to finish. And my legs mostly worked.

At 86km, I ran a 6:30min/km. Fastest one since the downhill around KM 55. At KM 88, I realized there was more than just 2 km left. I forced myself to eat some trail mix. At KM 90, I got a blip of service and texted my crew that I had 1 KM left. At KM 91, I begged for the bench overlooking the estuary to appear. At KM 92, there it was. I sat for a couple of minutes and realized I was cold and would rather be done than sleep. Because I closed my eyes laying on the bench. Not hoping I was done, but needing to regroup to bring forth my last reserves. And then you start walking again. Which turns into a shuffle. And ... not quite a run.

I hope she parked at the east end of the lot not where we started. She did. I saw the car. And a glorious outhouse! And done! A photo. A hug. A smile of satisfaction.

Not dark. Not sore (tired). Ready for something other than water. Pizza tasted good but hard to focus on eating. I got into warmer clothes and we drove off. 

After a shower, I had a hard time staying warm. Into bed solved that. I had weird dreams and trouble sleeping. Next day, I was mostly fine walking... stairs required a little extra attention and slower. Two days later, I could have run. I had an enforced two week break (no running or exercise, just walking).

Two weeks later, my legs are still a little heavy after my first couple of runs. But I'm dreaming of my next adventure: Wonderland Trail around Mt Rainier in August - 3 days, 93 miles, 22K elevation gain.

Which 100k next year? Will I come back for the Mendo 50k or run the 50mile course with modifications to be closer to 50? Is there a 50 miler in December I want to run? 

Friday, August 29, 2014

Unique IP in a log

I have a log file with fields and an IP address. I got asked to find the unique IPs today.

It took some internet searching to piece this together. So recording for later:

tail -1000 error.log | grep from | awk '{print $14}' | sort -n | uniq

Tail -N    Gets the last N lines from the log file. 
Grep       Filter out the lines for the ones I wanted. It has many lines without IPs

Those could be replaced with grep from error.log.
Or "cat error*.log"

awk is used to access fields in a record. This says...  show field 14.
That's the space delimited field were the IP is in this particular log.

sort sorts. uses -n to sort numerically.
uniq only keeps the unique records.

Wednesday, July 2, 2014

Code challenge: IP to country with redis

The challenge: 
Lookup country code by IP address using redis and python.

The solution:
A quick google search found several free sources of data.

I only care about country and not city or lat/lon or timezone. The "GeoLite Country" data will work. It is 95,000 lines of csv data updated once a month.

The format is "start IP","end IP", "start decimal", "end decimal", "country code", "country name". I'm using the csv module to parse it.

The redis sorted set takes a score and a unique element. Note that country code gets repeated in the data and so I made the country code unique by appending the IP.

Converting an IP address to decimal value would work easily with netaddr module. I don't have that on my mac, so I'm using the struct.unpack("!L",socket.inet_aton(ip))[0] method.

I also detect missing ranges in the data and insert "empty" for those ranges into the data.

The Lookup code:

import redis
import GeoIP 
red = redis.Redis() 
CC = GeoIP.ip_to_country("xx.xx.xx.xx") 
print CC 

GeoIP.ip_to_country(ip) will return the country code from the data or "empty" for empty blocks - say It will return "unknown" for things outside the range or invalid formatted IP addresses

The data is about 20MB.

The module:

# The GeoLite databases are distributed under
# the Creative Commons Attribution-ShareAlike 3.0 Unported License.
# The attribution requirement may be met by including the following
# in all advertising and documentation mentioning features of or use of this database:
# This product includes GeoLite data created by MaxMind, available from

import redis

def data_to_redis( myRedis, key, filename ):
    import csv

    # the data is csv. "start","end","d_start","d_end","country code","country"
    # the redis data is a zlist. note the country code repeats

    fp = open( filename, "r" )
    count = 0
    empty = 0
    lastEnd = 0
    csv_reader = csv.reader( fp )
    for line in csv_reader:
        #print line
            startIP,endIP,startDec,endDec,CC,country = line
            print line

        #print "{0} {1} {2} {3} {4}".format( startIP, endIP, startDec, endDec, CC )
        # use the startDec as the score
        score = int(startDec.strip('" '))
        endDec = int(endDec.strip('" '))
        if score-1 > lastEnd:
            # assume a missing block.
            #print "missing block: {0} to {1}".format( lastEnd+1, score-1 )
            myRedis.zadd( key, "empty|{0}".format(lastEnd), lastEnd+1 )
            empty += 1

        lastEnd = endDec
        # use CC|startDec as the
        member = CC.strip('" ')+"|"+str(score)
        myRedis.zadd( key, member, score )
        #if count > 10:
        #    print "early exit for debug"
        #    return
        count += 1
    print "added {0} records to {1}. empty blocks {2}".format( count, key, empty )

def ip_to_country( myRedis, key, ip ):
    dec = ip_to_dec( ip )
    data = myRedis.zrevrangebyscore( key, dec,0, num=1, start=0 )
    if len(data) > 0:
        CC,start = data[0].split("|")
        CC = "unknown"
    return CC

def ip_to_dec( ip ):
    import struct
    import socket
    "convert decimal dotted quad string to long integer"
    # note the big vs little indian packing
    return struct.unpack('!L',socket.inet_aton(ip))[0]

add some more error checking and perhaps create a CC to country name hash in redis.

Saturday, August 4, 2012

Dear Amazon

Dear Amazon. I realize this is an issue that impacts less than 0.001% of your customers. But we live in rural California and do not get mail delivery to our house. So we can either get USPS sent to our post office box or FedEx/UPS to our house address. Is it possible to flag our account to indicate mail only? Or FedEx only? Or more generally, that a PO Box can only be USPS? I realize you try to ship the cheapest way possible. I appreciate that. And when stuff comes, it is brilliant. But the stress of a package coming via FedEx to our PO Box that won't get delivered is dreadful. I'm not sure what language you write your software in, but perhaps one additional conditional would be able to resolve this for all of us on The Sea Ranch and other rural locations without mail delivery. Thanks for your help. bob

Sunday, May 27, 2012

Speech to Text with timeline data

I'm working on a book project where I have audio output of a reader and the associated text. I want to sync the two. My tool has a simple method... press the mouse down for each word as the audio is playing. Record the time. But sometimes (often), more fine tuning is needed. I figured that someone must have solved this already. I did a search and played with some tools. None of them were magic. The best thing so far is Audacity. A free audio tool. It has an automated bit that gets close. But I find having to edit that is harder than just going at it with the label track. My process: - zoom in. - use left-mouse and shift-left-mouse to change the selection. - then ctrl-left-mouse to play. - adjust the selection if necessary - ctrl-b to create a new label (I don't enter the work-- potentially useful) Walk through the text on a page. Export the label data. It creates a text file with two columns, start and end time. Adjusting my code to take start & end. And writing a perl/python script to convert to the Flash array. Only real question remaining is the format desired. The data files can be imported for editing. I think the logical thing to do would be to outsource someone. $5/page? It takes me about 10 minutes or so per page. Current book has 26 pages. I suspect I'll get faster.

Thursday, February 24, 2011

AMFPHP treats all ints as uints

Negative ints across the wire are deserialized as unsigned ints. Really big unsigned ints. Not sure how old of a version we are using.

Solvable though.

Sunday, January 30, 2011 - for reals?
Free basic system.