Wednesday 14 August 2024

JavaScript Destructuring Assignment Gotcha

This wasted several hours on me. I'll explain exactly what I was doing that got me into this mess a little later, but the bare essence of the problem that I ran into was this:-

let a = "this is a" let b = "this is b" [a,b] = ["foo", "bar"] console.log(a) console.log(b)

What would you expect it to output? I expected:-

foo bar

That's not what I got, though:-

$ node foo.js /home/eamonn/tmp/foo.js:4 [a,b] = ["foo", "bar"] ^ ReferenceError: Cannot access 'b' before initialization at Object. (/home/eamonn/tmp/foo.js:4:4) at Module._compile (node:internal/modules/cjs/loader:1358:14) at Module._extensions..js (node:internal/modules/cjs/loader:1416:10) at Module.load (node:internal/modules/cjs/loader:1208:32) at Module._load (node:internal/modules/cjs/loader:1024:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:174:12) at node:internal/main/run_main_module:28:49

The error-message made no sense to me. To fast-forward past lots of tearing-out-of-hair, changing the code to this (note the addition of the ";" on the second line):-

let a = "this is a" let b = "this is b"; [a,b] = ["foo", "bar"] console.log(a) console.log(b)

...makes everything work as it should:-

$ node foo.js foo bar

So what's going on? The answer is that the two lines...

let b = "this is b" [a,b] = ["foo", "bar"]

...are actually parsed as:-

let b = "this is b"[a,b] = ["foo", "bar"]

Note that b is being used to to try to dereference the "array" that is the string "this is b". But, of course, b isn't defined at this point so can't be used to dereference anything (and in this light the error-message makes more sense).

My JavaScript coding style is was to omit trailing ";" on the basis that they aren't necessary. It turns out that this is only true most of the time. I'm starting to seriously rethink that style now.

What was I doing? I had two "fetch" commands that I wanted to kick off in parallel. I had some processing to do that depended on the results of both of them. Rather than running them sequentially, I wanted to do this:-

(async () => { let first_response let second_response try { // Schedule the two fetches to run asynchronously const first_promise = fetch("http://my.server.ie/firstthing.json") const second_promise = fetch("http://my.server.ie/secondthing.json") // Wait for the two fetches to complete [first_response, second_response] = await Promise.all([first_promise, second_promise]) } catch(error) { ...do something if either of the fetch() calls craps out } // Process the retrieved data const first_result = await first_response.json() const second_result = await second_response.json() process_results(first_result, second_result) })()

But it would fail:-

$ node baa.js ReferenceError: Cannot access 'second_promise' before initialization at /home/eamonn/tmp/baa.js:12:79 [...]

Adding in that semicolon to clear up the "ambiguity" about the destructuring assignment solves the problem just fine:-

(async () => { let first_response let second_response try { // Schedule the two fetches to run asynchronously const first_promise = fetch("http://my.server.ie/firstthing.json") const second_promise = fetch("http://my.server.ie/secondthing.json"); // Wait for the two fetches to complete [first_response, second_response] = await Promise.all([first_promise, second_promise]) } catch(error) { ...do something if either of the fetch() calls craps out } // Process the retrieved data const first_result = await first_response.json() const second_result = await second_response.json() process_results(first_result, second_result) })()

...and the code works as it should

References

I got the clue I needed from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment. There is a particular sentence buried in there:-

If your coding style does not include trailing semicolons, the ( ... ) expression needs to be preceded by a semicolon, or it may be used to execute a function on the previous line.

Not exactly my issue, but close enough that it pointed me in the right direction. Thanks, Mozilla Developer Network, you are endlessly useful and informative.

Having seen the light, I did some research into the merits and demerits of relying on automatic semicolon insertion. The (now deprecated in favour of Typescript) Google JavaScript Style Guide is pretty emphatic (https://google.github.io/styleguide/jsguide.html#formatting-semicolons-are-required):-

Every statement must be terminated with a semicolon. Relying on automatic semicolon insertion is forbidden.

That's good enough for me.

Thursday 25 April 2024

try...catch...finally - Not (quite) what I expected

Consider this bit of code:-

def foo(): print("Called foo()") try: print("In the 'try' block") 10 / 0 except Exception as e: print("Got an exception: ", str(e)) finally: print("In the 'finally' block") print("About to return from foo() at the bottom") foo()

What do you think it will output? It probably won't come as a surprise:-

$ python3 a.py Called foo() In the 'try' block Got an exception: division by zero In the 'finally' block

Now consider this slightly variation:-

def foo(): print("Called foo()") try: print("In the 'try' block") 10 / 0 except Exception as e: print("Got an exception: ", str(e)) return print("In the 'except' block, after the 'return' statement") finally: print("In the 'finally' block") print("About to return from foo() at the bottom") foo()

Note the addition of the return statement to the except block. I would have bet money that it would behave differently: that the return in the except block would return straight out of foo(). and the finally block would not be reached. I would have lost the money:-

$ python3 a.py Called foo() In the 'try' block Got an exception: division by zero In the 'finally' block

The return statement in the except block does not exit foo(): rather it drops to the finally block. I can't claim credit for noticing this: my youngest son got caught out by this behaviour and told me about it.

One other thing to notice: in either case, if an exception occurs and is caught, foo() returns at the bottom of the finally block: the print statement at the bottom of foo() is never reached. Ofcourse, of no exception occurs, the finally block is still run and the rest of foo() runs as you would expect.

It got me to wondering if other languages behaved the same way. Let's try it in Javascript:-

function foo() { console.log("Called foo()") try { console.log("In the 'try' block") throw new Error("oops") } catch(e) { console.log("Got an exception: ", e) return console.log("In the 'catch' block, after the 'return' statement") } finally { console.log("In the 'finally' block") } console.log("About to return from foo() at the bottom") } foo()

It behaves in exactly the same way:-

$ node a.js Called foo() In the 'try' block Got an exception: Error: oops at foo (/home/eamonn/tmp/a.js:7:15) at Object. (/home/eamonn/tmp/a.js:22:1) at Module._compile (node:internal/modules/cjs/loader:1198:14) at Object.Module._extensions..js (node:internal/modules/cjs/loader:1252:10) at Module.load (node:internal/modules/cjs/loader:1076:32) at Function.Module._load (node:internal/modules/cjs/loader:911:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12) at node:internal/main/run_main_module:22:47 In the 'finally' block

Ofcourse, this is all well documented: it's just not exactly how I thought it worked. RTFM, people 😉

Wednesday 3 April 2024

Antenna Experiments - Reprise

This is a follow-up to my earlier earlier post: if you haven't already done so, I suggest reading that one first.

I caved and bought a NanoVNA. It is a useful piece of equipment for not very much money, if you have an interest in RF.

Anyway, hooking up one of those "DF Robot" 433MHz antennae to it produced some interesting (in that they differ sharply from the previous results) results:-

The maximum return-loss/lowest VSWR occurs at 436MHz. Between 433.5MHz and 438.5MHz, the VSWR < 1.5. This is much closer to the expected values and suggest that my previous attempts to measure antenna resonance using just a spectrum analyser are not to be relied-upon.

Turning to the antenna consisting of a bit of coax with 16.5cm of exposed inner conductor, recall that theory suggested that the resonant frequency of that should be 454.5MHz.  It turns out that reality concurs pretty closely:-



 That red marker is at 452MHz, which is pretty close. It also suggests that the velocity factor of the signal in the coax should not be considered when doing the calculation. It isn't completely obvious (to me, anyway) why not: my intuition suggests that it should. But nothing I have read supports this intuition and now the measurements also indicate that my intuition is wrong. If you know why, please do leave a comment letting me know.

Also, it looks like the "rubber ducky" antenna is a much better antenna than the piece of coax at all of the relevant frequencies.


Sunday 17 March 2024

Avoiding Arduino millis() Rollover Bugs

If you are using the millis() function to measure time on an Arduino, you might be tempted to do something like this:-

unsigned long timer_expires_at = millis() + SOME_DURATION; while (millis() < timer_expires_at) { [do other stuff while waiting for the timer to expire] } [do stuff that should happen after the timer expires]

For the most part, that will work well. But after around 49.7 days (4,294,967,296 a.k.a 232 milliseconds, to be exact), you might run into a problem. If millis() + SOME_DURATION exceeds 4,294,967,295, it will rollover and your comparison will match prematurely. Of course, you would have to be pretty unlucky for this to occur in practice, but - as Mary Chapin Carpenter put it - Sometimes you're the Louisville Slugger, sometimes you're the ball.

I had plans sketched out for a small C++ class to handle this elegantly when I decided to do a quick bit of Googling to see if I was reinventing the wheel entirely here and I came across the most beautiful simple, and elegant solution to the problem in https://arduino.stackexchange.com/questions/12587/how-can-i-handle-the-millis-rollover/12588#12588. It is a long, detailed reply and well-worth reading, but the gist of it is to just change the logic very slightly:-

unsigned long timer_started_at = millis(); while (millis() - SOME_DURATION < timer_started_at) { [do other stuff while waiting for the timer to expire] } [do stuff that should happen after the timer expires]

It should be pretty clear how this avoids the problem after ~49.7 days, but it may be less obvious why it doesn't just shift the problem to the other end.  Imagine that your Arduino has been powered on for 30 seconds, so a call to millis() will return a value of 30,000. You want to wait for 40 seconds, so you set SOME_DURATION to 40,000. Subtracting 40,000 from 30,000 in the condition of the while() loop will indeed cause a rollover (30,000 - 40,000 mod 4,294,967,296 = 4,294,957,296), but the result of that rollover will still satisfy the condition in the way we want.

Antenna Experiments

Let me state from the outset that my knowledge of RF and antenna design is very rudimentary. I basically consider it to be black magic.

With that out of the way, I read something a while back that made me wonder if it was possible to measure antenna resonance with a track-generator equipped spectrum analyser.  In particular, with two identical antennae - one connected to the track-generator output and one connected to the spectrum analyser input - would there be an obvious peak in response at the antenna's resonant frequency.  That is an experiment worth trying.  

Here is the initial setup:-


 It is sweeping from 0 - 1.5GHz and there is already a very obvious peak.  Looking closer:-

The antennae are intended for use with DFRobot APC220 radios which have a frequency-range of 418-455MHz.  So in the ballpark, even if the measured peak is kind of low.  There is a significant difference between performance at the two ends of that frequency range, though. Reducing the frequency span on the spectrum analyser from to 418-455MHz:-


There is nearly 15dB difference between 418MHz and 455MHz.  Still...I can't say for sure what the resonant frequency of the antenna is actually supposed to be: perhaps it is 407.5MHz and the result is completely valid.

For the second test, I replaced the both antennae with a simple monopole consisting of a piece of coax with 16.5cm of the shielding stripped back.  This should resonate at a wavelength of 4 × 16.5cm = 66cm.  Assuming the speed of light is  3 × 108m/s, that should be equivalent to 454.5MHz.  Let's see:-

Far from conclusive. The peak is at 152.5MHz, way over on the left.  There is a second peak at about 415MHz.  Zooming in to the 418-455MHz range:-


There is no sign of a peak of any kind near the resonant frequency.  

My "assumption" above about the speed of light wasn't entirely tongue-in-cheek.  The headline figure of 3 × 108m/s is the speed of light in a vacuum. Signals travel through coax cable significantly slower than this.  According to a datasheet for RG179 coax that I checked (I think that the kind of coax I have), the velocity factor is 0.7.  That would put the speed of the signal propagating through that coax at closer to 2.1 × 108m/s and - if the wavelength-to-frequency computation should take this into account - would put the resonant frequency at 318MHz.  I have no idea if the velocity factor should be applied here or not: if you know, please leave a comment.  There is certainly no bump in the output of the spectrum analyser at or near 318MHz that would give a clue either way.

Finally, I replaced the two coax monopoles with two lengths of wire, balanced precariously in the SMA connectors:-


The pieces of wire are identical and are both 10.5cm long. Looking at them as 1/4 wavelength monopoles and applying the equation, their resonant frequency should be 714MHz.  It doesn't appear to be:


There is indeed a strong peak, but it is nowhere near where it is supposed to be.  Interestingly, if I apply the velocity factor of 0.7, things look a lot better: that would imply a resonant frequency of 500MHz.  But I have no reason to believe that the velocity factor of a scrap of wire is 0.7, even if I knew that it was supposed to be included in the calculation.  I'm just fudging numbers here.

I got a similar result with 16.7cm of the same wire:


A good strong peak at 345MHz where is nowhere near the predicted 449MHz, but in the right ballpark if I apply the magic velocity factor of 0.7.

If you read down this far hoping for a satisfying conclusion and some enlightenment then I can only apologise for the disappointment you must be feeling.  I promise you, I feel your pain.

Nothing in any of this has done anything to shake my conviction that RF and antenna design is a black art.


Rotary Encoders

These are wonderful little things.

I got to thinking a bit about exactly how they worked, so I hooked one up to a power-supply and an oscilloscope.  There is 3V being delivered to the centre pin and one oscilloscope channel connected to each of the the two outer pins.  At rest, the centre pin isn't connected to either of the outer pins.  Turning the encoder clockwise...

...you can see that the blue trace goes to 3V a little before the yellow trace (the blue channel is the left-hand "A" pin on the encoder). 

Turning the encoder counter-clockwise and the opposite happens:-

Here the yellow trace (the "B" pin) goes to 3V before the blue one.  The key thing is that as the encoder passes over every detent, there is a point at which both pins are connected to the centre pin, but the direction of rotation determines whether the "A" pin gets there first or the "B" pin.

What sent me down this particular rabbit-hole was a very interesting pair of YouTube videos about using using just digital logic (i.e. no microcontrollers), here and here.  I had never previously thought about using these without a microcontroller and thinking about them in this way does reveal some interesting insights.

 

Wednesday 5 July 2023

Sweet Mystery

 This packet contains 12 sweets of 4 different colours (black, red, yellow and - my favourites - green). 

 

 
The number and order of the colours in each pack appears to be random (based on many, many samples 😉 ). If my calculations are correct and if they are really randomly distributed, one in every 4.2 million packets will contain 12 sweets of the same colour. Something I have often wondered about: how do the manufacturers avoid this? I can see a number of possibilities:  
 
  • Don't worry about it - it happens infrequently enough not to care
  • Optically scan each group of 12 sweets just before wrapping them and if they aren't sufficiently heterogeneous, don't wrap them - throw them back into the pot and try again. This only seems like it would be worth doing if they were optically scanning them anyway for other reasons (e.g. quality control)
  • They don't really throw all of the colours into one big pot and select them at random: they keep the individual colours separate right up to the packing stage and just shuffle the order in which they pick them to create the appearance of randomness.
Does anyone have any theories or insights? I'd love to know. In the meantime I'll keep eating them - purely in the interests of science, of course 🙂