Thursday, March 31, 2016

How to improve quality of legacy software

When developing software from scratch, I follow these steps: Use cases (aka concept of operation), requirements, design, code & unit tests, system test planning, system test.

Recently, I was asked how to improve the quality of a software written by other people a couple of years ago. I was informed that it was working, the only missing thing was compliance to software development procedures. I recommended to do the above sequence in reverse, i.e. first planning and executing full system tests. That way, we can answer the most important question, "is it working correctly". After we are satisfied with the tests, we can review and document the design and refactor code.

Even if we have to stop after testing, we will have added value to the existing product by being able to demonstrate in a repeatable manner that it is working. If we started the development sequence from the beginning and had to stop midway due to other priorities, we would have only wasted our time.

Wednesday, March 30, 2016

Model simplification strategies for testing

When developing complex models/algorithms, it is usually difficult to evaluate full model test results. The only option to verify complex model output is to compare it with some other implementation, for example Matlab toolboxes.

In addition to using 3rd parties for comparison, you should add functionality that lets you to simplify the model to a form whose results can be easily interpreted by a technical person. For example, if you are working on an algorithm that converts Geodetic coordinates to ECEF, you could have a function that temporarily sets the ellipsoid to a sphere (by making the eccentricity zero). It is easy to calculate expected results for a perfect sphere.

Similarly, if you develop a kinetic 6DoF flight simulation, you should have functions or flags that let you easily turn off complicating factors like aerodynamics (by multiplying coefficients with zero), wind, variable gravity, Coriolis, ellipsoidal Earth and terrain elevation. Your aim is to simplify your model to a ballistic flight in vacuum with constant gravity over a flat and non-rotating Earth. You can then use high school physics to calculate expected trajectories and compare them with your model outputs.

To clear doubts about your code, you should be able to quickly show that it obeys basic geometrical/physical laws.

Saturday, March 19, 2016

Error in NASA code

NASA World Wind java code is available on the internet. While looking at the their SDK2 EGM96.java file, I discovered that the simple linear interpolation code has an error. The original code:
...
double ul = this.gePostOffset(topRow, leftCol);
double ll = this.gePostOffset(bottomRow, leftCol);
double lr = this.gePostOffset(bottomRow, rightCol);
double ur = this.gePostOffset(topRow, rightCol);

double u = (lon - lonLeft) / INTERVAL.degrees;
double v = (latTop - lat) / INTERVAL.degrees;

double pll = (1.0 - u) * (1.0 - v);
double plr = u * (1.0 - v);
double pur = u * v;
double pul = (1.0 - u) * v;

double offset = pll * ll + plr * lr + pur * ur + pul * ul;
...

I prepared a graphic to easier analyze the algorithm:


As you can see at the end of above code snippet, the offset formula starts with pll*ll. pll is (1.0 - u) * (1.0 - v). The correct multiplier of the ll term has to be v*(1-u). The easiest way to fix the code is to swap the topRow and bottomRow indices when calling getPostOffset() function.

I guess the error was not detected since results do not differ too much because, although the points are used in wrong order, they are close to each other nonetheless, so the result might not have looked suspicious.

I tried to log a bug report on their Jira site, but I could not since I don't have an account. I was able to file a bug report on GitHub.

Lessons learnt:
  • Thoroughly test 3rd party code, even if it is from NASA or Mathworks. Every code is guilty unless proven innocent. Ignore this advice and you will find yourself chasing strange errors for a very long time.
  • Separate interpolation code into a function, write lots of unit test for that function to verify that it works correctly.

Good error messages

A good error message should contain the following:
  • Short description
  • What triggered the error?
  • What was expected?
Example: User inputs a negative value (-5) into a function foo that only accepts positive integers. A message similar to the following should be displayed:
Negative input!
The input value was -5. Function foo only accepts positive integers.

Tuesday, February 23, 2016

Çocuk yetiştirmek

Artık 3.5 yaşında olan oğlumuz Argun'un büyüme macerası ilginç derslerle doluydu. Bu derslerin Argun'un yapısı ile yakından alakalı olduğunun ve benzer çocuklarda (hareketli, neşeli erkek çocuk) daha çok işe yarayacağının farkındayım ama sanırım başka çocuklar için de geçerli şeyler vardır.

Başlangıçta çirkin bir ördek yavrusu olan Argun sonradan şirin bir çocuğa dönüştü:

Ana hatları ile gelişimi şu şekilde oldu:
  • 1. yaş: Yürüdü (herhangi bir yerden destek almadan beş adım atabildi)
  • 1.5 yaş: Kreşe verdik, ancak üç haftalık deneme süresinin ardından Argun'un henüz kreşe hazır olmadığına karar verip geri aldık.
  • 2. yaş: Cümle kurmaya başladı, emziği bıraktırdık.
  • 2.5 yaş: Kreşe tekrar başladı. Bezini bıraktırdık.
Yakın çevremdeki ebeveynleri izlediğimde çocukla ilgilenmenin çocukların özgürlüğünü ellerinden alacak boyutlara vardığını görüyorum. Muhtemelen onlara beni sorarsanız benim aşırı ilgisiz olduğumu söyleyeceklerdir (!)

Eski zamanlarda geçim derdiyle uğraşmaktan çocukla uğraşmaya pek zaman kalmıyordu, yedirecek şey sınırlıydı, götürecek doktor yoktu. İyi tarafı çocukların daha özgür olmalarıydı. Şimdilerde maddi olanaklardaki iyileşmeler ebeveynlerin ilgi, şefkat adı altında çocuklarının her işine karışan, sürekli kreşi telefonla arayan, en sıradan ateşte acillere koşan stres ve endişe küplerine dönüşmesine neden oluyor. Hem çocuklar zarar görüyor, hem de kendileri. Zaten yeterince zor olan şehir hayatı çocukla birlikte çekilmez hale gelebiliyor.

Çocuğun en önemli ihtiyacının özgürlük olduğunu düşünüyorum. Özgürlükten kastım kendi yardım talep etmedikçe veya geri dönülmez zarara uğrama ihtimali olmadıkça çocuğu rahat bırakmak. Küçük çocukların neredeyse tek öğrenme biçimi deneme yanılma. Denemesine izin vermek gerekiyor. Örneğin parka gitmişsek ve Argun birkez bile ağlamadan dönüyorsak bunu kayıp olarak görürüm çünkü sınırlarını yeterince zorlamamış ve yeterince öğrenmemiş demektir. Aşağıdaki videoda Argun bizim gözetimimizde iğne ile dikiş öğreniyor ve arada parmağına iğneyi batırıyor:


Bundan sonraki dönemde bizim işlevimiz Argun'a öğrenmeyi sevdirmek. Matematikten tarihe kadar her konunun ilginç bir özü, zengin gelişim öyküsü var. Biz bunları örneklerle, gezilerle, deneylerle verebiliriz. İlham için YouTube'daki Veritasium ve MinuteEarth kanallarına veya benim Science Experiments listeme bakılabilir.

Her çocuğun öğrenme biçimi farklı. Özellikle spor ve dans gibi bedenin karmaşık şekillerde kullanımını gerektiren aktivitelerde benim gibilere alt adımların anlatılması gerekir. Anlatılmayıp "benim yaptığım gibi yap" yöntemi uygulandığında başarılı olmam tesadüflere bağlı oluyor. Ortaokulda bir voleybol antrenmanında yeni gelen beden öğretmeninin benim bir hareketi yapamamam sonrası "sen nasıl okul birincisi olmuşsun, hayret" alaycılığı hala aklımdadır. "Seni başımıza öğretmen diye dikenlere lanet olsun" derdim içimden yüzüne karşı (!) Çocuğunuzun iyi bir öğrenim hayatı geçirmesini istiyorsanız bunu sadece okullara bırakamazsınız, kolları sıvayacaksınız. Hazır yeri gelmişken spor ve dans eğitiminde kullanılabilecek güzel bir kitap önereyim: The Inner Game of Tennis.
Dersleri listeleyecek olursak:
  • Çocukta karakter sorunu varsa ebeveyn önce kendine bakmalıdır. Kendizini iyileştirin yeter, çocukla uğraşmayın, çocuğa ozmozla geçer (!)
  • Ebeveynin rahatı da en az çocuğunki kadar önemlidir. Çocuğa ne kadar çok emek harcarsanız o kadar çok yorulursunuz ve çocuğa/eşinize/çevrenize kötü davranma olasılığınız artar. Enerjiyi idareli kullanın.
  • Ağlamasına müsaade edin ama ağlama için evin belli bir yerini kullabileceğini, her yerde ağlayamayacağını söyleyin. Ağladığı zaman kucaklayıp ağlama yerine bırakın, ağlaması bittiğinde geri gelebileceğini söyleyin.
  • Eğer yemek, banyo, uyku gibi önemli bir konuda çocuk itiraz ediyorsa bir-iki cümle ikna denenebilir, olmuyorsa aksiyona geçilir. Örnek: Sebze değil, pilav yemek istiyor. Sebze yedikten sonra pilav yiyebileceğini söylüyorum, tabii ki kabul etmiyor. Mutfaktan çıkarıyorum, ağlıyor, mutfakta ağlamanın yasak olduğunu, ağlama yerinin antre olduğunu söyleyip çırpınan Argun'u kollarından kaldırarak antreye götürüp bırakıyorum. Birkaç git-gelden sonra (15 dakika) ikna oluyor ve sebzesini yiyor.
  • Yemek yemeyi kendi talep etmeli, bir gün boyunca aç kalması göze alınmalı, iki öğün arasında yiyecek verilmemeli. Yemeği kendi talep ettiğinde hem yemeye ebeveyni tarafından zorlanıp özgürlüğü elinden alınmış olmaz, hem de yemekle sıkıntıyı değil, keyfi ilişkilendirir.
  • Çocuğa akıl vermeyi minimumda tutun. Örneğin yolda yürürken düşme riski var ancak düşerse en fazla bir yeri moraracaksa "düşersin" gibi uyarılarda bulunmayın. Parkta oynarken neyi nasıl yapması gerektiğini söylemeyin, kendisi düşe kalka keşfetsin.
  • Gün içinde çok sayıda eğitim fırsatı var. Örneğin masadaki kuruyemişleri yere saçmışsa bunun bir fırsat olduğunu hatırlayın. Kabukları toplamasını isteyin. "Topladım" dediğinde yerde kalan küçük parçaları gösterip "bunları da toplar mısın" deyin, bu işlemi en küçük parça bile toplanana kadar tekrar edin. Kritik olan sizin sabırlı, sakin ve kararlı olmanız. Böyle basit bir olay bile çocuğa dikkatten işini iyi yapmaya kadar çok şey öğretir.
  • Çocuğun işin içinde olduğu organizasyonlarda sakın acele etmeyin, geniş bir zaman ayırın, yoksa sabırlı olmanız mümkün olmaz.
Kolaylıklar dilerim.


Thursday, February 11, 2016

Finding the Intersection of Lines

The classical y = m*x+n line equation fails when finding intersections with vertical lines. Normally, the intersection point of two lines is found as follows:

Y = m1*x+n1 = m2*x+n2 --> x = (n2-n1)/(m1-m2)

Consider the following example, which clearly has an intersection at x = 1 and y = 0.5:

The classical equation for line AB results in m1 = infinity and n1 = -infinity, and for CD results in m2 = 0.5, n2 = 0. When you plug these values into x = (n2-n1)/(m1- m2), you get x = (0 - (-infinity)) / (infinity - 0.5) = infinity/infinity = NaN.

You could try to solve this problem using a bunch of if statements to check if a line is vertical, horizontal etc which would be prone to bugs. Or you could use parametric line equations:

Applying the above parametric formula to our example problem, we obtain the following equations:

Line1: P1 = A + (B - A) * t
Line2: P2 = C + (D - C) * u

To find the intersection point we equate P1 to P2:

A + (B - A) * t  = C + (D - C) * u.

Using scalar components:
Ax + (Bx-Ax)*t = Cx + (Dx-Cx)*u --> Ax-Cx = (Ax-Bx)*t + (Dx-Cx)*t
Ay + (By-Ay)*t = Cy + (Dy-Cy)*u --> Ay-Cy = (Ay-By)*t + (Dy-Cy)*t

Rearranging into matrix form:

| Ax-Bx, Dx-Cx | |t|  = |Ax-Cx|
| Ay-By, Dy-Cy | |u| = |Ay-Cy|

We can solve this M*TU = K system and obtain t and u values: TU = M^-1*K. t and u are -0.25 and 0.5 respectively. Finally to get intersection x,y coordinates, we can use

xIntersect = Ax + (Bx-Ax)*t
yIntersect = Ay + (By-Ay)*t

Which gives us the correct result of (1, 0.5)

An additional benefit of the parametric line equation is that it can easily be extended to three dimensional space. It is also used in calculating intersection lines with a planes.

Lesson learnt: Don't use y = m*x+n, instead use P = A + (B - A) * t

Wednesday, January 27, 2016

Basic Software Design Report

A software design report should contain as a minimum the following:
  • Definition of problem
  • Detailed description of inputs from outside and outputs to outside (definition, data type), i.e. API
  • Sequence diagram of main flow
  • Class diagram showing most important classes
  • Explanation of non-trivial algorithms