Monday, May 23, 2022

Lesson learnt moving from Google

Background

In the past I've has the experience of searching for information on the web and finding it in this blog. Of late I've not put a lot on this blog but I'm going start doing more as this blog is less empheral than Twitter and more public and searchable than Facebook. This has been prompted by moving my chipless.eu domain from Google where I have has to address some issues that others (or future me) might also encounter.

I've been using what was "Google apps for your domain" to host, for free, e-mail, calendars, and websites for my family and my business. Google decided that as from the middle of this year they would start charging for this hosting service and I decided I'd check what other options I had. I decided that I would use zoho.com as my new e-mail and calendar host. They seemed to be low-cost and, even so, offered more than I needed. I signed up a couple of weeks ago and I've started down the migration path. In the meantime, Google have decided not to charge for non-commercial use of their service, and so I'll be keeping my family stuff on Google. However, I'm still migrating chipless.eu

Cloud Service disabled by admin

I had to migrate chipless.eu e-mails from Google to Zoho. There's not a huge amount of historical e-mail and I could have done this "by hand" but I chose to follow Zoho's instructions. This involves using Google Cloud Services which I've never knowingly touched before. In the course of this I ran into a problem that took me a lot of time to over come. I could not create a "project". I enabled everything I could find that might be blocking it, I made sure that Google could charge me if I went over the hundreds of dollars of free service that was available, and still it seemed impossible to get my e-mail migration project set up. After many false paths, Google search led me to a StackOverflow article which asked for a solution to the same problem (unable-to-create-project-in-google-cloud-cloud-service-disabled-by-admin-plea). Google's error message was misleading, the problem was actually that there is an option ("Allow users to create projects") buried away in the Google Cloud Console which was unchecked. Once checked problem solved.


Sunday, June 07, 2020

Did COPVID-19 infections decline before UK lockdown?

My Facebook feed had a reference to an article in The Spectator which refered to a new study which claims that COVID-19 infections declined before the UK lockdown. This surprised me, so I thought I’d take a quick look. A web search found the source paper - an unreviewed publication by Professor Simon Wood, a statistician in the Mathematics department at Bristol University who has worked on the modelling of biological systems. Unreviewed papers can be poor, mistaken, and/or misleading, so I’m loath to sped time on them. On the other hand, the author seems to be respectable, I know a bit about mathematics, and I’ve spent some time looking at Covid-19 modelling and the impacts of lockdowns, so I felt like I should take a look.

I don’t feel confident to judge the details of the modelling techniques used in the paper, so I’ll leave that to the mathematical and epidemiological referees. But to summarise, he uses UK Office of National Statistics data on Covid-19 deaths in England and Wales (probably as good as it gets), together with his model of how deaths lag infections, to estimate the fatal infection rate. He concludes that the number of fatal infections peaked on the 18th March, 5 days before lockdown on the 23rd March. From that summary, one might conclude that there was no need for lockdown as things were already getting better.

However, although lockdown was declared on a particular day, I don't recall it being a  a sudden event - it seemed like part of a continuum. Several people I know had started taking lockdown-like precautions well prior to lockdown. For our (me and my wife) part, having flown in to the UK on March 15th, we left the house only once, to buy food, during the week prior to lockdown. Other people we know of, who'd seen the plague coming, took similar precautions. And we now know that some homes for the elderly took severe isolation measures a couple of weeks before lockdown. (Not only was this against the advice of the government, the authorities put great pressure on the home not to do this). So, anecdotally, things were locking down before "lockdown".

But we need more than anecodotes and it turns out that there is data which lets us assess the amount of effective lockdown in the run-up to lockdown. Apple and Google make available some data derived from mobile phone usage. For Apple it is routing queries, and for Google it is visits to types of location. The full datasets are available from Apple and Google, and they explain the data in more detail than I have. I’ve plotted UK data rather the paper's England and Wales data. 



For Apple Mobility Trends, 100 (%) is the activity level on 13th Jan 2020 and the other figures are percentages of that activity. For Google Mobility UK, 0 (%Δ) is the median value for the day of the week between Jan 3rd–Feb 6th 2020. This means the regular day-of-week fluctuations have been smoothed out in the Google data, but not in the Apple data.

I’ve added vertical lines showing lockdown (23rd March), the peak according to the paper (18/19th March), and the date at which a decline in activity sets in (8th March). The sole activity which increases after that date is the buying of groceries and pharmaceuticals (stocking up in anticipation of the lockdown). This is strong evidence that the expectation of lockdown brought about a decline in activity resulting in the the (projected) reduction in fatal infection rate from the 19th March.

Although it looks at cases rather than deaths, the work of Horace Dediu of Asymco.comis interesting in this context. He plots daily Covid-19 cases against Apple mobility data for many countries and US states. They are worth a look (note that Horave’s graphs plot Apple’s data in the opposite sense to mine)  and lend credence to the hypothesis that strict lockdown leads to reduced cases.

To conclude, in the case of the UK, peak fatal infection rate preceding lockdown, can be explained by the behaviour of the population anticipating a formal lockdown.

Monday, November 25, 2019

Register to vote

I’d been thinking it was time to start using the blog again when I felt compelled to encourage people to register to vote at the forthcoming general election. And I see it’s three and a half years since I blogged. Anyhow, the deadline to register to vote is TOMORROW – Tuesday 26 November at 11:59pmMake sure everyone you know has registered.

https://www.gov.uk/register-to-vote

Saturday, April 02, 2016

Squid

Never mind why but I have just been setting up some network related software on a Raspberry Pi. One of these softwares was Squid3. There are lots of guides out there to creating a web cache with a Raspberry Pi and I've been using several of them however, I ran into a problem that none of them mention....

My set up is that I have the Pi sitting on my home network. (Its name is rcjd-pi-2 and I can access it with the name rcjd-pi-2.local. The name works fine for ping-ing it, ssh-ing to it and setting it up as a web-proxy). I have a number of other machines on the network, for the purposes of this post, the only one which really matters is my laptop which is wired into the same access point (Apple Airport Extreme previous generation (n)). 

I set up Squid and set my laptop 's laptop's http proxy to be "rcjd-pi-1:3128". Running ps and netstat on the Pi showed that Squid was running and listening on port 3128. However, 
although a small amount of surfing got done (the first access to a site?) it turns out that subsequently access were be blocked. 

I checked and checked the squid.config file against the various instructions. Yes, I had 
  • http_access allow localness
uncommented as required, yes I had
  • acl localnet src 10.0.xxx.0/24
uncommented and the xxx set correctly. But something was wrong.

Then, glancing between the access log and the configuration file I noticed there was an option to uncomment
  • acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machine

in the configuration file. Strangely the log contained entries with IPv6 addresses in them such as
  • fe80::225:4bff:fec9:2118 TCP_DENIED/403 3976 GET http://images.apple.com...

Could that be it?

Yes! 

It still leaves open the question of whether I need to allow accesses from the IPv6 local private network range. I suspect it might be the correct thing to do but I'll leave it closed for now.



Friday, June 12, 2015

One month with an Apple watch


On April 11th I posted “How to justify an Apple Watch purchase” as a public rationale of why I’d ordered an Apple Watch. The watch arrived a little less than four weeks later which means I’ve now being using it for a month. I’ve worn it every day; it goes on as soon as I’m awake enough and comes off when I bathe, go to bed or (spoiler alert) change the strap. It stays on when I’m in the gym, as monitoring exercise was my key rationalisation for buying it. I think I’ve had enough experience now to make it worth recording my thoughts.

I’ll start by considering it as a watch, that is a timepiece worn on my wrist. Purely functionally it has one disadvantage compared to my electromechanical watch - the watch face is only visible when I move my wrist to angle the face towards me and the watch correctly detects that motion. It’s the second part that’s the problem, there’s what seems like only a 90% hit rate. Otherwise the watch is great, it tells the time and tells me the day of the week and date - pretty much all that my previous watch did. 

“But Roger”, you say, “what about having to charge it?”. Firstly, the battery has lasts all day for me (except when I fail to charge it). Secondly, I already have to deal with charging my phone every night (and during the day, more often than not), and since I don’t wear my watch in bed, charging isn’t a problem. Until all my gadgets (phone, laptop, toothbrush, shaver, …) run for a coupe of weeks without charging, the charger and cable isn't a problem.  

The watch feels good to wear. The rubber (“custom high-performance fluroelastomer”) straps are comfortable, warm (unlike the metal strap of my previous watch) and luxurious (honest). There is a noticeable difference between my (original) black strap and my white strap; the white feels softer and warmer when you put the watch on although I can’t say I notice the difference after a couple of minutes. 

The black-watch/black-strap combination is very black and very discrete. It looks good with casual clothes or a very black outfit; ideal for the Cadbury’s Milk Tray man. With a smart suit the combination is too understated; hence my purchase of the white strap. The black-watch/white-strap combination  looks good and is certainly not discrete. The proof of the difference - apart from the first day when the guy sitting next to me also had a new Apple Watch, no one has commented on the watch when I’ve been wearing the black strap, whereas I’ve had a few “Is that one of those watches?” comments when I’ve been wearing the white strap.

The watch offers ten different faces, all of which can be customised to some extent. There are five faces with hands to indicate the time (“analogue”) - three are fairly standard styles, one is the “Mickey Mouse” which I’d use if I were wearing a Micky Mouse tie, and the other face is a “chronograph”. The remaining five faces all use digits to display the time, although two (“Solar” and “Astronomy”) have designs which also give an analogue indication of the time. One of the digital designs (“Modular”) looks like it was designed to be a caricature of a digital watch. As I’ve worn a watch with hands for most of the past 50 years (I suppose it’s possible there was a short period when I had a digital watch) I use the “Simple” face with medium details. As well as the time this face shows the day and date, the outside temperature, the amount of battery remaining (currently 55%) and an indication of my current exercise status. The devices indicating these extra details are called “complications” and can be chosen by the wearer. If I were travelling I might choose to show local time on the hands and the time back home on one of the complications. Similarly, if I had a busy diary I might choose to show my next appointment. The complications not only show some piece of information, they can be used to switch to a watch app. For example, if I tap on the temperature (currently 25C), the weather app opens.

If you read “How to justify….” you’ll know I wanted the fitness features of the phone. Other than telling the time, the health and fitness features are the most obviously useful features the phone. That is, I think it is completely non-obvious (for example) that it might be useful to send a text message from your phone, whereas I think being able to measure your heart rate is an obviously useful function. I’m really pleased with the fitness aspects of the phone. I say "aspects" because there are two. The first aspect is using the watch when taking exercise. You select the “exercise” app, choose your activity, set any goal (time, distance, or calories) and press start. While you are exercising the watch measures and records your heart rate (frequently). When you’ve reached your goal the watch lets you know; alternatively you can tell the watch when you have finished. The watch then presents you with your statistics and they can be stored away if desired. The second aspect is that the watch is always recoding your activity and measuring you heartbeat, albeit less frequently than when exercising. Over the day it keeps track of how much you “exercise” (elevated heart-rate), how many calories you use, and whether you stand each hour. You can set goals for each metric. The watch keeps track of how close you are to meeting your goals, and the “fitness” app displays this using three concentric rings which fill with colour as the metrics increase. If and when you meet your goals the watch congratulates you. I like this gamification of healthy exercise - it makes me walk more and causes me not to sit slumped at my desk for hours on end. One advantage of integrating fitness functions into the watch rather than using a separate fitness band (FitBit or whatever) is that  you do not get the phenomenon where after a month or so you stop using the fitness band; it’s always there when you wear the watch.

The first thing which takes the Apple Watch way beyond a combination timepiece and fitness band is its utility as a communicator. You can make and take calls on the watch and send and receive messages. That is provided your iPhone is nearby. 

[My home has a landline, a DECT base-station and a handful of DECT cordless phones (handsets). The base station is wired to the landline and connected wirelessly to the handsets. I can take and make calls from the handsets as if they were connected to the landline but, in fact, I am relying on the DECT base-station to provide the connectivity to the phone network. I mention this because the relationship between the iPhone and the Apple Watch is similar. The watch works perfectly well as a communications device for phone calls and messages but it relies on the iPhone to provide its cellular and network connectivity. The watch connects to the iPhone via some combination of Bluetooth LE and Wifi. Exactly how they work and when they work isn’t completely clear to me but the resulting connectivity is better than if just Bluetooth LE were used].


If I get a call on my iPhone, my watch taps me on my wrist and I when I raise my wrist, I can see the caller id. I can red-button the call by touching the red button, or I can take the call by touching the green button.


To make a call you bring up the “Friends” screen and select the person you want to call. You then get the option to phone, message or magic them (if they’ve also got a watch).  


The watch works as a speaker phone - there’s no need to speak into the watch to be heard, nor to hold it to your ear to listen to the call. I have to admit that I still pretty daft when I use the watch as a phone but I’m sure with practice I’ll get used to it. You can read incoming messages on the watch. For outgoing there are a number of canned messages (e.g. “I’m on my way”) or you can speak and have Siri convert it to text, or send it as a voice message. 



Siri works well on the watch. Perhaps Siri is as good on the phone now, but Siri is much better on the watch than it used to be on the phone. You can use Siri for all sorts of things on the watch and it’s much easier to do so than to fiddle with the buttons and small touch screen. The only thing I can’t do that I’d expect to do is to dictate e-mails.

The second thing which distinguishes the Apple Watch from a mere timepiece is that underneath it all it is a computer (as an iPhone is a computer). I’ve mentioned in passing two ways in which the computer functionality can be accessed - via complications and via Siri. You can also access computer functionality via “glances” and via apps. Glances primarily provide information but they can also take input. I have just a few glances enabled some of which let me check my heart rate, access my fitness information, see my diary, check the weather forecast, and control music playing on my iPhone. With the current (first) version of the watch’s software, apps are more limited than on the phone, however, I have a number of useful ones installed - Passbook, Shazam, Uber and the iPhone camera remote control. 

I realise that I’ve got all this way without mentioning notifications. The watch can notify you about pretty much the same things as the phone. It is really useful to get some notifications on the phone. What you realise after a short while is that you must be far more selective on the watch than on the phone. I still need to get my e-mail notifications under control - I get far to too many; I want to know when I get a new e-mail from family, not when I get a special offer from Laphroaig.
What else to say?

Firstly the 13-amp plug/USB adaptor is a work of art - Apple’s finest. It would be even better it had two USB outlets.

Secondly, despite my tweet the straps are really easy to put on and take off - nothing like the nightmare of every other watch strap I’ve ever known.  

Thirdly, the watch really does free you from the phone. You don’t need to be looking at it so often. And I think glancing at your wrist is a little more socially acceptable than pulling out your phone and staring at it.

Finally a niggle. The way units are displayed in the Activity app on the iPhone isn’t right. Rather than letting you select miles or km in the app, the app determines the unit from the geographical settings on the phone. So the UK gets miles. Having spent some time puzzling and googling over this, I know I’m not the only person this affects. I’ve had a useful e-mail exchange with Jay Blahnik of Apple about this and it's being looked at.

Should you buy one? I don’t know but I’d buy one again. Maybe not the same model though.


Saturday, May 30, 2015

Wearable wearables

A little over a year ago I said
Forget watches, health sensors and the rest. Clothes - as in fashion - are going to be the end-game in wearables. is an interesting article electronics and fashion. Although the iMiniSkirt retails for £3,750, I don't think it will be too long to move wearables from CuteCircuit to Primark.
Being a technologist, not a fashion designer, I missed seeing the opportunity offered by accessories (belts, shoes, bags). These are not constrained by size, weight and the need to feel comfortable when worn, in the same way a clothes, and I assume that's why the aforementioned CuteCiruit now has a range of accessories.
 But back to clothes, Google have just exposed Project Jacquard, which promises to deliver fashion compatible textiles which can be used as sensors. This makes clothes that respond to the wearer's motions and emotions another step nearer. 


Sunday, April 12, 2015

AxesDrawer.swift

I read once (Dave Winer?) that if you’re programming you should blog about what your doing so that when, in the future. you can’t understand what you did, there’s somewhere to turn to. My motivation is slightly different, I’d like other people to be able to find what I’ve done, and its seems to me that blogging about it will serve that need. As background, I’m following the Stanford University CS193p course (““Developing iOS 8 Apps in Swift “) on iTunesU, and I’ve found that one of sample pieces of code that Stanford hand out is bugged. I’ve failed to find a way of reporting the bug and now that I’ve fixed it, I thought it would be friendly to blog about it.

As part of Assignment III: “Graphing Calculator” you are given the code for an AxesDrawer class to use as part of the project. The AxesDrawer class provides a function 

   drawAxesInRect(bounds: CGRect, origin: CGPoint, pointsPerUnit: CGFloat)

which draw, and label, those portions of the x and y axes with origin “origin” which sit within the bounds “bounds” of the current view. 

The first thing to state clearly is that the code as supplied by Standford works functionally; if you use the code it draws axes correctly. I did not find the problem from using the code, only from reading the code to try and understand how the code worked. 

The tricky part of the code is labelling the axes. The code has to decide the x (or y) increment between labels, and it then has to draw all the labels which are visible. The decision about the increment between labels is made on the basis of the size of the labels and the current scaling of the axes. The code computes the increment in terms of the number of points (drawing elements) per label. The variable pointsPerHashMark is used to hold this value. 

The mechanism behind the labelling of the code is straightforward. I’ll first explain it with the assumption that the origin sits within the bounds of the current view, then I’ll explain how it works when the origin is not within the bounds. The idea is that the code works from the origin outwards. The first set of potential labels are at (pointsPerHaskMark, 0) and (-pointsPerHaskMark, 0) on the x-axis, and (0 ,pointsPerHaskMark) and (0, -pointsPerHaskMark) on the y-axis. The second set of potential labels are (2*pointsPerHaskMark, 0) and (-2*pointsPerHaskMark, 0) on the x-axis, and (0 ,2*pointsPerHaskMark) and
(0,-2*pointsPerHaskMark) on the y-axis. And so on. Of course, at given distance from the origin, not all of the labels may fall within the bounds, and so the code checks whether each potential label falls within the bounds before plotting it.

   if let leftHashmarkPoint = alignedPoint(x: bbox.minX, y: origin.y, insideBounds:bounds)
   {
      drawHashmarkAtLocation(leftHashmarkPoint, .Top("-\(label)"))
   }

Eventually, at some distance from the origin, all of the potential labels fall outside the bounds, and the labelling can stop. The way the code controls the loop which performs the labelling is interesting. Initially the code sets up a rectangle (CGRect) whose centre is at the origin and whose x and y dimensions are pointsPerHashMark * 2; that is the bounds of the rectangle (in fact a square) are +/-pointsPerHashMark.  

   var startingHashmarkRadius: CGFloat = 1

   …

   // now create a bounding box inside whose edges those four hashmarks lie
   let bboxSize = pointsPerHashmark * startingHashmarkRadius * 2            
   var bbox = CGRect(center: origin, size: CGSize(width: bboxSize, height: bboxSize))

On each iteration the size of the rectangle is increased to (by 2*pointsPerHaskMark in each dimension)) so as to include the next set of potential labels. 

   bbox.inset(dx: -pointsPerHashmark, dy: -pointsPerHashmark)

The code detects termination of the labelling process by testing whether the view (area to be plotted) sits entirely within this rectangle. Once it does, any further potential labels must sit outside the view and so do not need plotting.

   while !CGRectContainsRect(bbox, bounds)

Now lets looks at what happens when the origin is outside of the view. The mechanism used in the code will work, albeit inefficiently. Because labels are only plotted if they are in the view, no spurious labels are plotted. But more interestingly, if we start labelling from the origin outwards, and use the code’s termination test, all the necessary labels will appear and the code will terminate. As the size of the rectangle (bbox) increases on each iteration, it will eventually contain the view, at which point all labels will have been plotted and the loop will terminate.   

So to efficiency. The first potential inefficency is do any work when there are no axes to plot. This is dealt by a simple test of the start of drawAxesInRect

   if ((origin.x >= bounds.minX) && (origin.x <= bounds.maxX)) ||
      ((origin.y >= bounds.minY) && (origin.y <= bounds.maxY))
  
The second inefficiency occurs when there is an axis to plot but the origin is outside the view. Suppose the origin is at (-100, 0), the view has a minimum x value of 0 and we have decided that labels will be plotted every 10 points. In this case the view will contain a portion of the a-axis starting at 100 with labels at 100, 110, etc. If the code does nothing to deal with this case, it work out from the origin and try (and fail) to plot labels at distances of 10, 20 and up to 90 points from the origin until it starts to plot at 100 points distant from the origin. An optimisation that can be done is to detect the case that the origin is outside the view

   if !CGRectContainsPoint(bounds, origin) {

and then determine how far the first plottable label is from the origin. This is what the code attempts to do and where the bugs occur.

   let leftx = max(origin.x - bounds.maxX, 0)
   let rightx = max(bounds.minX - origin.x, 0)
   let downy = max(origin.y - bounds.minY, 0)
   let upy = max(bounds.maxY - origin.y, 0)
   startingHashmarkRadius = min(min(leftx, rightx), min(downy, upy)) / pointsPerHashmark + 1

The first bug is that one of left, right, downy or upy must be zero, hence min(min(leftx, rightx), min(downy, upy)) is also zero and startingHashmarkRadius is 1. To see that one value must be zero, without loss of generality, consider the case where the x axis will be plotted. Again, wlog, consider the case where the origin is to the left of the view. In this case origin.x - bounds.maxX will be negative and hence leftx will be zero. I won’t speculate about why the code got written like this, but unless testing beyond simple functional checks were done, this bug wouldn’t be caught.

There is a second bug also present which I ran into when I fixed the first one. I’ll leave it as an exercise until later.

Reusing the case above, for the x-axis, origin.x - bounds.maxX will be negative, but bounds.minX - origin.x, the distance of the origin to the view, will be positive. Looking at the y dimension, bounds.minY - origin.y will be negative, as will be origin.y - bounds.minY since the y-coordinate of the origin sits between the min and max y-coordinates of the view. The distance of the origin from the view is, therefore, the one positive value amongst the four. Can can therefore, take the maximum of the four values. Accordingly my first attempt at a fix was 

   let rightx =   origin.x - bounds.maxX
   let leftx  = -(origin.x - bounds.minX)
   let downy  =   origin.y - bounds.maxY
   let upy    = -(origin.y - bounds.minY)
   startingHashmarkRadius = (max(max(leftx, rightx), max(downy, upy) / pointsPerHashmark) + 1

When I used this version the effect was weird. I’d pan a view, pulling the graph and axes across the screen until one axis went out of bounds. At this point the other axis stopped panning (although the plotted graph continued panning) but the labels incremented each time I had panned by more than the distance between two labels. This was the second bug. The problem is that the code computes the distance of the origin to the view, whereas what is needed is the distance of the origin from the first label. The fix is to the floor of the division to get an integral number of labels and then to add 1. So, the code which fixes the second bug looks like

   let rightx =   origin.x - bounds.maxX
   let leftx  = -(origin.x - bounds.minX)
   let downy  =   origin.y - bounds.maxY
   let upy    = -(origin.y - bounds.minY)
   startingHashmarkRadius = floor(max(max(leftx, rightx), max(downy, upy) / pointsPerHashmark) + 1

Search This Blog