Tuesday, January 29, 2019

How to outsource

The major benefit of outsourcing is to do more with less personel, to be shielded from human resources management issues and concentrate on the product. However, outsourcing work that is not clearly defined (like most research & development work) can be a headache if done haphazardly. By its very nature, most R&D work fails and should be treated as learning experiences. How can you outsource something that will most probably fail? I suggest dividing the task into the following three phases:
  1. Analysis: Concept of Operations, detailed requirements and acceptance test plans are generated.
  2. Design: Design documents and a minimum viable product that implements basic functionality is produced.
  3. Production: The end product covering all requirements is tested according to the acceptance test plan.
Instead of a single contract encompassing all three phases, each phase has its own contract. At the end of each phase, in order to continue to the next phase, both the contractor and the customer have to say GO. After that, the contract for the next phase is signed. The contractor gets paid only for the phase it has finished.

Note that for phases 1 and 2, there are no acceptance criteria for the deliverables, because adding additional criteria will drive the cost up. The contractor could submit a one page document and call it "requirements". The only leverage the customer has during these phases is the threat of not awarding the next phase and blacklisting. Therefore, most of the payment should be done in phase 3. If the total cost of the project is 100%, phase 1 should be 10%, phase 2 should be 20% and phase 3 should be 70%. Due to the relatively low cost, phases 1 and 2 could be awarded to multiple contractors to have some kind of competition.

Dividing the work into three phases protects the customer from mediocre contractors. At the end of phase 1 or phase 2, if the customer is not satisfied, armed with lessons learned and documents / prototype at hand, it can select another contractor. This phase structure also protects the contractor from fuzzy requirements and challenges that turn out to be technically impossible.

Friday, January 18, 2019

Good Variable names

Good code requires minimal comments to understand it. One of the best ways to achieve clarity is to use good variable names. Examples of bad variable names:
  • euler
  • h
  • azimuth
  • acc
Better variable names:
  • eulerRFB321NED2Body_rad: Rotated frame based Euler angles that will transform the NED frame to body fixed frame. Unit is radians. Note that array element order should be 0 = yaw, 1 = pitch, 2 = roll to reduce potential confusion.
  • hMSL_ft: Height above mean sea level. Unit is feet.
  • azimuthCWFromTrueNorth_deg: Azimuth measured clockwise from true North. Unit is degrees.
  • a_bi_NED_mps2: acceleration of body with respect to inertial frame, expressed in NED frame. Unit is m/s^2

Tuesday, January 15, 2019

Job attitude

Recently some novice engineers complained about their work, saying that it was boring. My own attitude towards my work is first and foremost "am I useful, do I add value". If I am not able to answer that question with a resounding yes, I have to find ways to make myself useful. Only after that do I allow myself to ask for more.

If you take initiative instead of complaining, you will go a long way, especially in fast growing environments, where there are many new challenges whose solutions nobody knows yet.

Similarly, if someone outside my workplace asks for my services, my first question is never "how much will you pay me" but "how can I be of help to you, is this problem worth solving".

As long as you keep on adding value, responsibility, respect and money will follow.

Sunday, January 13, 2019

Refactoring legacy embedded code

Recently we had to add new functionality to existing embedded software. When we analyzed the code, in many cases there were little to no documentation and code smells were all over the place. It was difficult to use existing functionality because it was very hard to understand. But replacing them with new clean code was not an option becaue the functions were used in many places. We would have to repeat costly HWIL and field tests, setting us back for months.

We decided to leave the existing functions but added comments about our suspicions, marked them as "deprecation candidate", wrote new ones that were only called from new code. We also heavily documented our additions. This approach is similar to changing public APIs: You rarely delete, you usually add and mark old code as deprecated so that existing software continues to function with the new API version.

To be able to update design/architecture, we first had to improve comprehensibility of existing code by removing clutter with these steps:
  1. Renamed variables, functions and classes to make them easier to understand.
  2. There were a lot of repeating code due to creation of new classes by copy-pasting old classes. We moved common code to utility classes/namespaces which made classes a lot lighter.
After performing the above two steps for a couple of months, we had a good grasp of what the code was trying to do. Think of it as discovering detailed requirements. Then we deleted dead code and out of date comments. Gradually we came up with simpler design ideas that would satisfy requirements.

Friday, January 11, 2019

Operational Amplifiers

Op-amps are used to amplify analog signals. By signal it is usually meant voltage, not current. So, op-amps amplify voltage. Their output current is low, on the order of mA.

In a control system, we usually want to drive a high current device like a motor or an electromagnet according to analog sensor (e.g. 49E) readings. Sensors output low voltage values which are amplified by an op-amp. In order to drive high current devices, we feed the op-amp output to a transistor that can handle higher currents. The reason we can't completely replace the op-amp with the transistor is that a transistor usually expects larger voltages at it's base (low current is not a problem) which the sensor cannot provide.

If your sensor output is not analog but digital (e.g. MPU6050 which has I2C output), you have to feed the output to a microcontroller which than can produce a PWM signal fed to the transistor, i.e. there is no op-amp in the digital sensor case.