Toto Slice

As my son went about cutting a mega bread slice, I told him that was called a “Toto slice”. And then immediately went onto explaining what is the origin of that phrase and why the heck nobody uses it except me.

Well, story starts ages ago when I got my first dog. It was a black terrier and a spitting image of Toto from Wizard of Oz. And Toto slice wasn’t named after him because he didn’t like bread. He liked chickens. Our chickens.

So, within a month, Toto was pronounced incompatible with our yard and gone. Where? I am not sure as some secrets run deep in my family but official version is that he was sold to somebody else. Whether it was an upstate farm or a backyard, the end result was the same - I was dogless.

Some time after, a replacement arrived. It was a mix of a female Scottish shepherd and a male white terrier. Rumors are that conceiving happened due to the bet between owners of each in regards to the capabilities of terrier. If they are true, terrier was skilled enough and thus a few puppies became available soon after.

In any case, the new dog was completely white and would quickly grow close to the size of a (smaller) Scottish shepherd. And his name was Toto too. It was shame to throw away a good name despite this dog being as far as possible from the Wizard of Oz namesake being both white and large.

Life wasn’t always easy and, while my family never went hungry, there was no extra money for dog food. Toto ate what we ate - more precisely, he ate leftovers. When leftovers were sparse, he would get a huge thick slice of bread dipped in the melted lard. That became known in the family as a Toto slice (or “totovska šnita” in my native language).

And Toto slice wasn’t just for a dog. Whenever my sister or I wanted a large piece of (ideally warm) bread, we would use the same phrase. And that’s the phrase I kept using for years and well into my current 40’s.

This phrase might live on with my children along with a story. Or it might die off with me. Regardless, I find it really interesting how sometime words we personally take for granted might not be known even to those closest to us. And how private the language can be even in these global times.

Transparent Mikrotik

Wyze Base Station created an interesting conundrum for my network. Unlike most IoT devices, it requires a cable connection. Place where I wanted to place it didn’t have any. So, the choice was to either bring a new cable or figure some other method. Of course, some other method it is.

As often, my hammer for those situation was Mikrotik device. In this case mAP lite. Idea was to create a transparent bridge (or as close to it as possible) between the Base Station and my network. Ideally, it would even keep MAC address of base station.

This is what I ended up going with:

/interface wireless
set [ find default-name=wlan1 ] ssid=^^SSID^^ mode=station-pseudobridge frequency=auto 

/interface wireless security-profiles
set [ find default=yes ] authentication-types=wpa2-psk wpa2-pre-shared-key=^^key^^

/interface bridge
add name=bridge1 protocol-mode=none

/interface bridge port
add bridge=bridge1 interface=wlan1
add bridge=bridge1 interface=ether1

First step was to switch wireless into station-pseudobridge mode. While it has a lot of warnings (mostly due to L2 bridging being tricky with wireless), it’s actually ideal for this situation as one device it connects to will be the sole user of it.

Once wireless security has been setup, all remaining is to create a bridge with both wireless and wired interface.

And that’s it.

Couldn't Find a Valid ICU Package

As I ran my .NET 5 application on Linux, I was greeted with the following error:

Process terminated. Couldn't find a valid ICU package installed on the system. Set the configuration flag System.Globalization.Invariant to true if you want to run with no globalization support.
   at System.Environment.FailFast(System.String)
   at System.Globalization.GlobalizationMode.GetGlobalizationInvariantMode()
   at System.Globalization.GlobalizationMode..cctor()
   at System.Globalization.CultureData.CreateCultureWithInvariantData()
   at System.Globalization.CultureData.get_Invariant()
   at System.Globalization.CultureInfo..cctor()
   at System.Globalization.CultureInfo.get_CurrentCulture()
   at System.Globalization.NumberFormatInfo.get_CurrentInfo()
...
Aborted (core dumped)

Underlying cause was my ancient Red Hat installation missing localization support and the easy way to deal with it is was to simply set DOTNET_SYSTEM_GLOBALIZATION_INVARIANT environment variable. On command line that would look something like this:

DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 ./myprogram

However, if we really don’t need globalization support, setting it directly in .csproj might be better:

  <PropertyGroup>
    <InvariantGlobalization>true</InvariantGlobalization>
  </PropertyGroup>

Welford's Algorithm

Most of the time, general statistics calculations are easy. Take all data points you have, find the average, the standard deviation, and you have 90% of stuff you need to present a result in a reasonable and familiar manner. However, what if you have streaming data?

Well, then you have a Welford’s method. This magical algorithm enables you to calculate both average and standard deviation as data arrives without wasting a lot of memory accumulating it all.

So, of course, I wrote a C# code for it. To use it, just add something like this:

var stats = new WelfordVariance();
while(true) {
    stats.Add(something.getValue());
    output.SetMean(stats.Mean);
    output.SetStandardDeviation(stats.StandardDeviation);
}

Algorithm is not perfect and will sometime slightly differ from classically calculated standard deviation but it’s generally within a spitting distance and uses minimum of memory. It’s hard to find better bang-for-buck when it comes to large datasets.

One Taken Every Second

In order to keep an eye on my garage I placed a Wyze camera in it. So when I noticed one day that somebody has swiped my tool bag, I thought I’ll find the culprit quickly. Well, it was not meant to be.

I had recording turned on but only a 32 GB card in it. And I noticed tool bag missing only after two weeks or so. So any recording was already gone by the time I took a look. Since only other people that had access to the garage were Amazon delivery guys, I could narrow it down but not sufficiently to do anything about it.

So I went to look into a cheap solution to record images long term and RTSP immediately came to mind. Even better, Wyze cameras already support it (albeit via a special firmware).

My idea was to simply record an image every second and save it on my NAS using ffmpeg. While this was a simple task in theory, it proved to be a bit annoying to find parameters that would be stable enough. Most notably, sometime it would just stop ingesting new frames and thus require restart.

After testing a lot of different parameters, I came with the following code:

while (true); do
    ffmpeg \
        -probesize 1000000 \
        -analyzeduration 1000000 \
        -flags low_delay \
        -fflags discardcorrupt \
        -re \
        -rtsp_transport tcp \
        -stimeout 10000000 \
        -allowed_media_types video \
        -i rtsp://${CAMERA_USER}:${CAMERA_PASS}@${CAMERA_IP}/live \
        -f image2 \
        -strftime 1 \
        -vf fps=fps=1 \
        -async 1 \
        -vsync 1 \
        -threads 1 \
        -use_wallclock_as_timestamps 1 \
        "${BASE_DIRECTORY}/${CAMERA_IP}~%Y-%m-%d-%H-%M-%S.jpg"
    sleep 1
done

Using this setup ffmpeg will keep taking image every second. If it gets stuck, it will exit and then it’s on while to restart the capture again. One can then use a separate process to convert these files into a mjpeg file but that’s story for another day.