Friday, October 20, 2017

ADSL2+ Sync Rate

If you have ever called up your ISP support team to complain, you may have heard them quote a number called SNR to describe the quality of your connection.

It's not quite as simple as a single number, though.

ADSL2+ uses multiple "channels" at once to transfer data between the ISP and your home modem. In the case of my ASUS modem, there are 512 channels: there is 2.208 MHz of available spectrum on the copper wire; these are split into equally spaced 4.3125 kHz wide channels.

However, not every channel (or bin) is created equally: there is almost certainly some electrical "noise" or congestion on the cables. Some bins are reserved for normal telephone connections, others for upstream and downstream communication with the server, with a few reserved as guards to keep sections separated.

I pay for a 4 Mb/s connection, so let's assume the ISP allows me 1672 bits' worth of downstream sync rate*. It is up to the modem and ISP to negotiate how to allocate the sync rate across the available bins. My modem surveys the ratio of signal to noise (SNR) across each bin in the downstream set of channels. A rule of thumb is that each bit requires about 3dB of SNR to reliably decode the signal without errors. Bins with higher SNR will therefore have higher bit capacity for bits.

My modem allows me to connect with SSH and look at three files, which are ordinarily used by the administrative web server interface to present me a way to easily change modem settings and view diagnostics:
  • /var/tmp/spectrum-bpc-ds
  • /var/tmp/spectrum-bpc-us
  • /var/tmp/spectrum-snr
Let's do some very simple mathematics with the data in these files that won't be too difficult to follow:

Data taken from the SNR file:
1672 bits to allocate
268 channels into which they must be allocated
10,523.27 dB (the sum of SNR across the available reception channels)

Assumptions:
3 dB/bit required to convert received signal without errors

1) 1672 bits * 3 dB/bit = 5016 dB SNR required to decode the signal

2) 10,523.27 dB - 5016 dB = 5507.27 dB unused SNR

3) 5507.27 dB / 268 channels = 20.54951 dB unused SNR per channel

4) Now, we iterate each of the 268 channels in turn. I'll demonstrate with the first channel, where the SNR was 24.21 dB.

a) 24.21 dB - 20.54951 dB = ~3.66 dB to be used for data reception

b) 3.66 dB / 3 dB/bit = ~1.22 bits

c) round 1.22 bits to a whole number: 1

It turns out that this simple algorithm gets us remarkably close to the numbers of bits per channel (BPC) in the other files, which are arrived at by the modem's own algorithms. The difference could even be accounted for by (lack of, or outdated) "bitswap", where bins are periodically re-checked for SNR and reallocated to make the best use of the spectrum as it evolves over time.


* Ignore - for now - that those numbers are not equivalent, they are effectively the same thing quoted for different units of time
 

Wednesday, August 23, 2017

OpenVPN

This article on Ars Technica inspired me to try and setup a VPN, but it seemed to lack a couple of extra steps that were required to run the server on a Raspberry Pi behind a router. The official OpenVPN documentation was helpful but long-winded, and I resorted to a few askubuntu answers in the end.
  1. Certificate names

    client-no-pass doesn't have to be replaced with the client host name, but can be (pretty much) any identifier for the client. The important thing is to sign all certificates with the same CA key.
  2. VPN topology

    The default topology of net30 is perhaps not as easy to comprehend as subnet, and subnet works with Ubuntu, Windows and iPhone clients.
  3. Default gateway

    On the server, I needed to push redirect-gateway, push "route xxx.xxx.xxx.0 255.255.255.0" and consequently push "dhcp-option DNS xxx.xxx.xxx.1" so that the client would be able to route to the server's local network, and the tun0 device would be able to do DNS lookups.
  4. Firewall rules

    Besides enabling the forwarding of IPv4 so that the Raspberry Pi acted as a router, I need to add three firewall rules in iptables: one for NAT masquerading, and two for accepting new and related established connections.
  5. DNS setup for clients

    The dhcp-options that were pushed to the client were ignored, which resulted in the client being unable to resolve any DNS names to IP addresses. This was only a problem on Ubuntu clients, but the script /etc/openvpn/update-resolv-conf was provided when I installed openvpn; all I had to do was reference it from client-name.config as a pair of lines:
    up /etc/openvpn/update-resolv-conf
    down /etc/openvpn/update-resolv-conf
    

Sunday, August 13, 2017

Grub

It's nice when the Grub bootloader remembers your last choice and reuses it as the default value next time your machine boots. Using Linux, put the following in /etc/default/grub:
GRUB_DEFAULT=saved
GRUB_SAVEDEFAULT=true
Then run:
sudo update-grub

Wednesday, March 29, 2017

SubscribeOn / ObserveOn

Once you have an instance of an IObservable<T> object, Reactive Extensions (Rx) provides at least two points where you can choose an IScheduler which will determine the threading context under which the later operations will run. Those operations can be broken down into two phases: subscription and observation.

Subscription refers to the operations that modify the behaviours of an IObservable<T> (e.g. Throttle, Merge, Concat) The IScheduler for the subscription phase is assigned by the SubscribeOn() method.

Observation refers to the callback operations that are invoked as the result of observing an IObservable<T> (e.g. OnNext, OnCompleted, OnError) The IScheduler for the observation phase is assigned by the ObserveOn() method.

While not the subject of this post, you can also choose an IScheduler implementation to convert any IEnumerable<T> to an IObservable<T> with the ToObservable method. This is relevant when the IEnumerable<T> has the potential to take a long time to be enumerated.

Saturday, March 04, 2017

6 Simple Rules for Async/Await

  1. If a method A calls another method B that returns a Task (or Task<T>) then the calling method does not block on the completion of that task, unless it either:
    1. awaits that task, or
    2. waits on the result of that task
  2. The calling method cannot await a task unless it is declared with the async modifier, which causes the compiler to build a state machine (similar to the IEnumerable iterator) for that awaitable method.
  3. In point 1 above, the behaviour is independent of whether or not B is marked with the async keyword
  4. It is this non-blocking behaviour of A that allows the calling thread to proceed past the point where B is invoked. It's even possible for the thread's call stack to unwind, and for the thread to go back to reading the Windows message queue if it was the UI thread.
  5. The compiler will only warn you "because this call is not awaited ..." if B was marked with async.
  6. It is expected that A will do something with the Task returned by B; at the very least there should be some code to check that the Task did not throw any exceptions. If - instead of B - we have an async method C that returns void then we do not present A with any opportunity to monitor the completion of C. Unobserved exceptions thrown during the execution of C could indicate corrupted program state and can be configured to terminate the application in much the same way that an unhandled exception in synchronous code can unwind a stack fully and terminate the process.