MATLAB Techniques: Self-identifying (by type) methods

We all know MATLAB by default fill numbers with 0 if we haven’t specified them (such as expanding a matrix by injecting values beyond the original matrix size). Cells are default filled with {[]} even if you meant to have cellstr()  {''} across the board. Sometimes it’s not what we wanted. 0 can be legitimate value, so we want NaN to denote undefined values. Same as cellstr(), we don’t want to choke the default string processing programs because one stupid {[]} turns the entire cell array into to a non-cellstr array.

For compatibility reasons (and it’s also hard to do so), it’s not a good idea to simply modify the factory behavior. I have something similar to table() objects that requires similar expansion on arbitrary data types, but MATLAB’s defaults proves to be clumsy.

Instead of writing switch-case statements or a bunch of if statements that relies on type information like this:

function x = makeUndefined(x)
  switch class(x)
    case {'double', 'single'}
      x = NaN(size(x));
    case 'cell'
      if( iscellstr(x) )
        x = repmat({''}, size(x));
      end
    % ...
  end

I found a slick way to do it so I don’t have to keep track of it again if I need the same defaults in other places: take advantage of the fact that MATLAB selectively will load the right method depending on the first input argument(s)*:

Create a commonly named method (e.g. makeUndefined()) for the PODs and put it under the POD’s @folder (e.g. /@double/makeUndefined.m, /@cell/makeUndefined.m). The functions look something like this:

function y = makeUndefined(x)
% This function must be put under /@double
  y = NaN(size(x));
function x = makeUndefined(x)
% This function must be put under /@cell
  if( iscellstr(x) )
    x = repmat({''}, size(x));
  end

Similarly, you can make your isundefined() implementation for @double, @cell, etc, just like the factory-shipped @categorical/isundefined() corresponding to the same rules you set for makeUndefined().

Actually, the switch-case approach is analogous to the common abuses of RTTI in C++: every time a new type is added, you have to open all the methods that depends on the type info and update them, instead of having the classes implement those methods (with proper object hierarchy and overloading).

[Scott Meyers, “Effective C++”] Anytime you find yourself writing code of the form “if the object is of type T1, then do something, but if it’s of type T2, then do something else,” slap yourself


This technique is especially valuable when you and TMW (or other users) have different ideas of what an English word (e.g. empty, defined, numeric) means. Like do you consider boolean (logical) numeric? TMW says no through isnumeric().

To give you an example, I made a tool to nicely plot arbitrary features in my @table over time (the equivalent of @timetable before TMW introduced it). It only make sense if the associated dependent variable (y-axis) can be quantified, so what I meant is broader than isnumeric(): it’s isConvertibleToDouble() since I casted my dependent variables with double() in between.

Boolean (logical) and categorical variables have quantifiable levels, so double() can be applied to them, they should return TRUE for isConvertibleToDouble() despite isnumeric() returns FALSE. They have the same behavior for basic types like double(), single(), char(), cellstr(), struct(), etc.

In summary,

  1. You say what you really mean (by introducing nomenclature), NOT what it typically does
    – this is like creating another indirection like half(x) instead of directly writing x/2 or x>>1.
    – spend 90% of your time coming up with a very intuitive yet precise name. ‘Misspoke’ == Bug!
  2. The new data types self-manage through implementing methods used by your code.
    – assume nothing about input type other than the interfaces that are accessed through
    (the traditional approach knows exactly what inputs they’re going to see)
    – if you did #1 correctly, there’s no reason to foresee/prepare-for new input types
    (just implement the methods for the input data types that you want it to run for now)
    – no sweep (switch-otherwise) case to mishandle** unexpected new input data types
    (because it won’t run on an input data type until all called methods are implemented)
    – introducing new input data types won’t break the core code for existing types.
    (new input data types can only break themselves if they implemented the methods wrong)

* This is tricky business. MATLAB doesn’t have function overloading (by signature), but will look into the type/class of FIRST dominant input argument and load the appropriate classes. Usually it’s the first argument, but for non-POD classes, you can specify the dominance relationship (Inferior classes). Actually little has been said about such relationship in PODs in the official documentation.

I called support and found that there’s no dominance relationship in PODs, so it’s pretty much the first argument. That means this trick does not work if you want to overload bsxfun() for say, nominal() objects (which doesn’t have a bsxfun() implementation) keeping the same input argument order because the first argument is a function handle for both the factory and the user method. Bummer!

[EDIT 2025/04/16]: Found new official documentation about the design consideration of MATLAB’s OOP explicitly saying it’s the first (leftmost) argument that decides which function that shares the same function name gets called:

This is why the new ‘*_fun‘ functions I write, I always use the object to operate on as the first argument whenever possible. Gets a little bit ugly when I want to support multiple inputs like cellfun(), so I have to weight whether it’s worth the confusion for the overloading capability.

** Unless you want to torture yourself by listing all recognized types and make sure the ‘switch-otherwise‘ case fails.

Loading

Quote of the day: You can’t learn too much linear algebra

Benedict Gross, Professor of Mathematics at Harvard said in his E-222 lecture: ‘You can’t learn too much linear algebra’.

If you read my WordPress pages in Signals and Systems, I’m trying a new approach to explain the materials done under the classical approach in a much more compact way using basic ideas in linear algebra.

Linear algebra is fun and easy to learn (at intro levels), once you get used to the fact that it’s a carefully picked set of restrictions that most physical problems boils down to. In high school algebra, it’s disguised as ‘method of detached coefficients’ when you solve systems of simultaneous equations.

Once you model your problem into linear equations (the hardest part), you will see the equivalence (or mathematically equivalent analogs) of different problems ranging from economics to circuits and mechanics. The beauty of the ‘detached coefficients’ approach separates the context of the problem from its mathematical structure as the application specific variables are often grouped as a vector (or matrix) that you deal with as a unit. In fact, most problem boils down to this:

    \[ \bf{Ax = b} \]

It’s your job to construct \bf{A}, \bf{x} and \bf{b} and tell people what they mean.

I agree with Gilbert Strang, Professor of Mathematics at MIT that it’s time to make linear algebra the standard curriculum over calculus. Digital control used to be a very advanced graduate class, but after a few decades, it’s undergraduate material. Linear algebra has very few knowledge dependencies (hard pre-requisites), so it’s great material to teach at any level.

Loading

Agilent Infiniium (Motorola VP22) Motherboard USB Pinouts for DOM SSD

The Motorola VP22 motherboard used in a variety of modern Agilent’s Infiniium series oscilloscope (the ones with Megazoom) and logic analyzers has a 5-pin USB port that’s not quite the same as the standard headers on most motherboards.

The reason I needed the pinouts of the USB port is because I have a 40-pin DOM SSD to replace the old clunky mechanical hard drive, which needs 5V for power.

I don’t want to modify/resolder the original wire harness (for the LS-120 and CD-ROM drive) nor I have the molex connectors to make a power splitter. Luckily, I found somewhere that I can safely tap a 5V for low power devices: the internal USB header. Here’s a picture with the DOM SSD module wired:

VP22 USB Power Tap

This post serves as a reminder to myself that the ground is the second pin to the left and 5V is the rightmost pin. The pinout diagram as follow:

Loading

(Front-end) Attenuator Autopsy of HP 54616B Digital Oscilloscope

One day I got a call from a friend saying that he accidentally fed high voltage signal (line voltage) and broke one of the channels.

My hunch is that since he didn’t feed kilo-volts into it, the damage is limited to the (front-end) attenuator (an expensive part). I had an extra unit lying around so I just swapped it for him so he can get it up and running.

Once I got the time, I opened it up the broken unit to see the damage. My hunch was correct:

Fried 1NB7-8303 54616B Attenuator Block

The 500Mhz attenuator used in 5461XB, 54505B/54506B/54510B/54512B (has to be version ‘B’), 54520/54540 series, and early Infiniiums 54810 series, E1428A (VXI board), 166XCS/166XES (MSO), 167XG-003, etc. are all the same: 1NB7-8303 (or 1NB7-8458). For 5461XB, it’s tucked inside 54616-68401*, which is a big metal case that covers 2 attenuators and an external trigger PCB (see the picture on the right hand side). Everything else is tucked individually inside 54512-63402 metal shield.

In this autopsy, the resistive trace of 1NB7-8303 was blown open-circuit (charred), so bad that it spot-welded the metal casing.

I took a good attenuator and measured resistance that the blown open ‘trace’ should have been. Turns out it’s a little less than 1Ω so I used 4-lead Kelvin sensing from my Agilent 34401A multimeter. I calibrated that meter using my Data Precision 8200 reference and agreed with my Fluke DMM, so hopefully I wasn’t way off.

I could have put a small SMD resistor matching the resistance of the blown trace, but since I have a few back up attenuators lying around, I’d rather not risk the signal integrity by doing so, since I cannot guarantee the temperature specification if I just replaced it with a resistor. The scope was up and running in perfect condition after replacing the attenuator.

Now you know what happens when you feed line voltage to a scope. The damage is usually limited, but the front-end (attenuator) is the most expensive RF part that makes a scope truly a scope (see Dave’s rant on EEVblog). The ADC hybrid can get just as expensive, but usually the attenuator takes the hit before your input gets to fry the ADC.


* Be very careful about a similar looking block with similar number 54503-63401 or 54503-63402. It has the same metal housing, but inside the attenuator chip 1NB7-8116 or 1NB7-8288, which is NOT compatible with first generation Infiniiums, later 54500 series or 5461XB series. They are used in 54502A, 54503A, 54504A, 54510A.

[EDIT 2025-04-25]: There’s a newer revision of 54054A that has the power switch in the front (the serial numbers do not start with a country code). They use 54512-63401 (the newer attenuator design) instead of 54503-63401 and they are not compatible forward or backward.

54501A never showed up in the discussion because 100Mhz scopes does not need the individually shielded metal block. The attenuator was directly soldered to the main assembly.

Loading