Showing posts with label TV. Show all posts
Showing posts with label TV. Show all posts

Friday, 12 September 2014

Wi-Fi in Schools

A friend of mine asks :

OLPC Class - Mongolia Ulaanbaatar
Question : What's the effect on the human body, of 20 children in a classroom, each downloading a 3 minute youtube video over Wi-Fi?

An excellent question!


As with all good science, let's start with an experiment.  I happen to have a 3 minute HD video on my network, so I can time how long it takes to copy across to my laptop:

missingbytes:$ time copy /NetworkDrive/HDVideo.mp4 .
real   0m6.550s
user   0m0.001s
sys    0m0.116s

So a single 3 minute video (3:19 to be precise) will use about 6.5 seconds of Wi-Fi time to copy.

With 20 students, and rounding up a little to account for congestion..

... lets call it 200 seconds of Wi-Fi activity total.

Transmit power

The transmission power for Wi-Fi signals is heavily regulated in the EU, the US and also in New Zealand where I'm performing the test.

The maximum 2.4-GHz transmission power is regulated by law, so lets assume it's 20dBm = 100mW = 0.1W ( source )

As we all learnt when we were in school, a watt is a joule per second, so 200 seconds at 0.1W is 20J.

Now we know that a class room of children downloading a youtube video results in 20 joules of microwave energy being emitted from the Wi-Fi router's antenna.

A brief diversion : Ionizing and Non-Ionizing radiation



Electromagnetic radiation forms a spectrum, from low frequency and radio waves, up through the microwaves, visible light, X-rays and on to gamma rays which have very high frequencies indeed.

Those high frequencies are characterized as ionizing, they're very dangerous to humans and their ability to cause DNA damage and ultimately cancer is well known. This is the reason why we need to be so careful around medical/dental imaging devices, and need to take precautions such as wearing sunscreen and polarized sunglasses when we're outdoors on a sunny day.

It's not necessarily the amount of energy, it's more the frequency that's the problem.  This high frequency ionizing radiation quite literally has the ability to rip electrons off their atoms.  It's these "ions" which go on to cause damage to biological systems.

By contrast, the lower frequency non-ionizing radiation (such as used in Wi-Fi, or FM radio) doesn't have the same ability to affect us in this way.

By itself, non-ionizing radiation can only cause heating in biological systems. Indirectly, it's this heating which slows down or speeds up chemical reactions and/or signalling within the cell, and it's these secondary effects which has the potential to cause problems.

Intuitively, this is why the 1000 watt microwave oven in your kitchen makes food super hot in a few minutes using microwave energy, but it doesn't actually make your food radioactive.  (You'd need an X-Ray oven for that!)

Anyway, lets continue, we've got 20 joules remember?

Absorption


Now we need to make a pretty unrealistic assumption. Suppose that the entirety of those 20 joules of energy was somehow absorbed by one child.  Of course, this can't happen in the real world for two fairly obvious reasons:

  • A router transmits energy in all directions.  For all the energy to be absorbed by the child, the router would somehow need to be inside the child.
  • Microwave energy interacts only weakly with the human body.  That's one of the great benefits of Wi-Fi, it can pass right through walls and ceilings and straight through you and me.

But just for fun, lets continue on anyway and figure out what would happen if all of those 20 joules were absorbed by one child.

An average 6 year old child weighs about 22 kilograms.  (Of course, my 4 year old son also weighs 22 kilograms, but that's a blogpost for another day!)
The human body is about 65% water, so lets consider 14 kilograms of water.

The specific heat capacity of water is 4.18 J / gK

So we have 20 J / (14,000 g) / (4.18 J / gK) = 0.00034 K = 0.0004 °C
(That's 0.4 millikelvin for all you geeks out there.)

Answers!

So there we have it, even with a wildly exaggerated assumption:

Answer : A classroom of children, all downloading a 3 minute youtube clip over Wi-Fi yields a maximum biological heating due to 2.4GHz microwave radiation of 0.0004 °C.

(0.4 millikelvin is about twice as small as it's possible to measure using a precision thermometer.)

Conclusion


We shouldn't really be too surprised.  Wi-Fi signals are incredibly weak. Consider this, those 20 joules of microwave radiation is the same amount of chemical energy contained in one thousandth of a teaspoon of sugar.

There's no way to prove scientifically that microwave radiation from Wi-Fi is safe in the human body. Science doesn't work that way. You can't prove a negative.

But we can try and make smart choices about tiny risks.


For example, the exposure from a banana is about 0.1 μSv of harmful ionizing radiation because of their high quantities of naturally occurring radioactive potassium.

Yet who thinks twice about giving bananas to kids in schools?

Thoughts, questions or especially corrections?  Please feel free to leave a comment down below!

Saturday, 2 June 2012

The Cocktail Party Effect (Part 2 of 2)

Late at night, when the boys are (finally) asleep, we like to be able to hear the voices of the people on the television without waking up the neighbors.  In part one I described how to build a custom digital audio filter by specifying a frequency response and running an optimizer to determine the optimal coefficients.

This post looks at one of the actual filters I use on my television in more detail.

First up, here's the response curve for the filter.
White = Filter,   Blue=Desired Response
You can see the extremely strong cut-off starting at 1000Hz, dropping very quickly, -10 dB at 300Hz, and -20dB at 50Hz.  In the other direction, we have an almost flat response between 1000Hz and 5000Hz, and then a very gradual drop, -3dB at 10kHz.

This corresponds nicely with the human voice, which ranges between 800Hz - 8000Hz, with the majority of the sound energy between 1500Hz-4000Hz

Here's the actual code I use to set the desired frequency:

    float GetDesiredResponse(float freq)
    {
        float logFreq = log(freq);
        float cutOff0 = log(1000.0f);
        float cutOff1 = log(3400.0f);
        float cutOff2 = log(8000.0f);

        float result = 1.0f;
        if(logFreq  < cutOff0)
        {
            float factor = logFreq / cutOff0;
            result *= pow(factor, 10.0f);
        }
        if(logFreq > cutOff1)
        {
            float factor = 2.0f - logFreq / cutOff1;
            result *= pow(factor, 2.0f);
        }
        if(logFreq > cutOff2)
        {
            float factor = 2.0f - logFreq / cutOff2;
            result *= pow(factor, 2.0f);
        }
        return result;
    }


As you can see with this method, it's relatively easy to get precise control over the frequency response.

A couple quick notes:
  • It's important that the frequency response be a quasiconcave function.  This ensures there are no kinks in the response, which will (1) cause visiting audiophiles to complain, and (2) make some voices more difficult to comprehend.
  • Be careful when specifying a very steep transition, or trying to completely stop-pass some frequencies.  A digital filter has some pretty strict limits on the kinds of things it can filter.  Like the proverbial genie, if you try and go there, the optimizer will give you exactly what you ask for.
  • I can't hear past ~17kHz, so I took extra care to test with high frequencies, but played back at half speed to make sure I wasn't torturing the cats and dogs.
  • This is also a good time to read up about Odd and Even functions, which correspond to odd/even numbers of coefficients in the polynomial.  An Odd function, for example, will start at -∞ and rise to +∞.  That's useful if you want a high-pass or low-pass filter, but bad for a band-pass filter.
  • Having problems with the y racing off to infinity? Your Y polynomial may have an unstable feedback loop.  Try a longer or shorter filter, or add an A-stability criteria to your optimizer.

 

Weighting

So you're probably wondering why the filter matches the desired function so accurately over the vocal range, but seems to drift in other areas.  Here's the weighting function I use to compute the error:

That is, I sample 129 frequencies logarithmically spaced between 9Hz to 18000Hz.  For each frequency, I add the square of the L2 error, weighted by the square of (desired response + a quarter).

Why so much squaring?  To help the function minimizer converge to a global minimum, it's nice to give big parabolas for the optimizer to slide inside.

Oh, and before I forget, here's the actual filter in C++, licensed under CC0:

    // From http://missingbytes.blogspot.com/2012/06/cocktail-party-effect-part-2-of-2.html
    static float x[5] = {0};
    for(int i=0; i<length; i++)
    {
        float sampleLeft = GetNextLeftSample();
        float sampleRight = GetNextRightSample();

        for(int j=4; j>0; j--)
        {
            x[j] = x[j-1];
        }
        x[0] = sampleLeft + sampleRight;

        // The filter!
        static float y1=0.0f, y2=0.0f, y3=0.0f, y4=0.0f;
        f32 y0=+0.588746*x[0]-0.492725*x[1]-0.757061*x[2]+0.661062*x[3]
            +1.242758*y1+0.545942*y2-1.044236*y3+0.238844*y4;
        y4=y3;y3=y2;y2=y1;y1=y0;

        float value = y0*volume;
        float outValue = bound(value, -32768, 32767);
        if(value != outValue){clippingCount++;}
        unsigned short emit = (unsigned short)outValue;
        *dest++ = emit;// Output left
        *dest++ = emit;// Output right
    }


Results

So what does it sound like?  Well, for music, pretty bad actually :)  But that's not the point!  It's designed for voice, and that where it really shines - even though the voice quality sounds a little unnatural, you can understand what people are saying, even at very low volume levels.

    (Oops, having some video encoding problems - updated video coming real soon now)

The Future

Can we do better than this?  Yes, we can!  When I finally get around to setting up a 7.1 audio system, I plan to measure the room response function using a calibrated microphone.  Then it's a simple matter of taking the FFT of the incoming audio, dividing through by the room response, then taking the inverse fourier transform before sending to the sound card.  Normally this would entail some delay/lag, but that's not a problem when playing buffered video.  I could even have different responses for different listener locations, or dynamically respond to changes in temperature or humidity.

... watch this space for a future update.




Saturday, 26 May 2012

The Cocktail Party Effect (Part 1 of 2)

Late at night, when the boys are (finally) asleep, we sometimes like to watch stuff on the television.  If the TV is too loud, the boys wake up, and that really breaks immersion.  If the TV is too quiet, we can't hear the gripping dialogue, and that breaks immersion too.

  Here's how I fixed it in code.

Mono

Okay. Don't judge me, but the first thing I did is to convert the incoming stereo audio down to mono.  Most music tends to be well spatialised, while the voice track comes through the center channel.  By converting to mono I estimate a ~3dB drop in perceived volume during musical interludes, or, conversely, I can raise the total volume by 3dB without waking up the boys.

Bass

The next thing to do is to cut the low frequencies.  The rumbles and the explosions.  The ones which reverberate throughout the house and wake up the kids and the neighbors too.

It's relatively easy to find info on constructing a digital high-pass filter on the internet, provided you can cut through the jargon.  Generally there tends to be an incoming signal, denoted as xn.  It's the series of samples coming from your video decoder.  And then there's an outgoing signal, yn.  It's the series of samples that you send to the sound card.

Then there's some function that links them together.  Here's an example of a really simple high-pass filter you can find on the internet :

yn = 0.2 . xn  + 0.8 . yn-1

It's a simple matter to turn this into code:

A simple high-pass filter, in C++.

Z-transform


To work out the frequency response, we can use the Z-transform to find the Transfer Function, which maps our filter from the discrete, time domain, into the continuous, frequency domain.  In our case:

H(z) = Y(z)/X(z) =  0.2 / (1 + 0.8 . z-1)

The cool thing is we can use theory to treat z like a complex number, even though in practice, all the xn and yn will be floating point numbers.  For example, to compute the frequency response at 1000Hz, with a sample rate of 48kHz, we take:

i = √-1
FrequencyResponse(1000Hz) = | H(2π1000/48000 i) |
= | 0.2/(1 + 0.8 / (2π1000i/48000) ) |
= 0.008

If we draw this on a graph, with frequency (Hz) on the X-axis, and magnitude (dB) on the Y-axis, we can get a "Bode Plot" of our filter:
A Bode Plot for a simple filter.
So that's a great start, but now we want more control over which frequencies pass through the filter, and which are blocked, and by how much.

Optimize

Back in the old days, we would have had to endlessly try different filter combinations and painstakingly compute Bode plots to find combinations of filters that might fit our requirements.

But now that we all have super-computers under our desks, we've got much more powerful tools to solve an old problem in a new way.

In pseudo-code, it looks like this :

def GetDesiredFrequencyResponse(frequency):
    // Your desired EQ function goes here

def EvaluateFilter(filter):
    errorSum = 0
    for frequency in 20 .. 22000:
        freq1 = GetDesiredFrequencyResponse(frequency)
        freq2 = filter.CalcFrequencyResponse(frequency)
        errorTerm = freq1 - freq2
        errorSum += errorTerm * errorTerm
    return errorSum


bestFilter = Minimize(FilterFactory, EvaluateFilter)
That's right, lets just use an offline optimizer to compute the optimal co-efficients for us!  You could use numpy, or octave for this.


... And Generate the Code

'f32' is a 32-bit float
Now we have our filter coefficients, we need to inject it back into C++.

Here's a function which writes out the C++ directly.  We can just copy and paste directly into  ProcessAudio(), hit recompile, and hear the results immediately.

Notice the coding style - it's rife with buffer overflows, and makes huge unjustified assumptions about the inputs.  You certainly couldn't use this code in a production environment, but as the scaffolding to bootstrap a single-use filter, the iteration speed trumps all other considerations.



Sample


Here's a sample output :

    static float y1 = 0.0f, y2 = 0.0f;
    float sum = 0.211989*x[0]
        -6.794531*x[-1]
        -29.803024*x[-2]
        +0.535980*x[-3]
        +0.215555*x[-4];
    float y0 = (sum -9.433180*y1 +31.500277*y2) / 0.000017;
    y2 = y1;
    y1 = y0;


Coming Soon

So this is part 1 of 2, in the next post I'll go into the details of the actual filters I'm using on my television, as well as some sample video so you can hear before and after.  I'll try and include tips for how to ensure convergence, and some gotchas that the Bode plot won't tell you about.  And of course, if there's anything you'd like to know more, why not suggest it in the comments below?