Statistika 101: Memberikan Pengertian Hasil Quick Count Pilpres 2019

Disclaimer: Saya bukan orang praktisi statistik maupun orang yang memiliki background di bidang statistik. Saya adalah lulusan S1 Teknik Elektro yang pada kurikulumnya hanya mendapatkan mata kuliah “Probabilitas dan Stokastik” di mana topik statistik hanya dibahas oleh dosen pengampu saya dalam 3 kali pertemuan sebagai pengantar sebelum masuk materi kuliah tentang Probability Density Function

Terlalu Panjang; Tidak Membaca (TL;DR): Dugaan pak Ronnie Rusli terhadap kebutuhan sampel yang lebih banyak dari 2000 untuk mencapai margin of error serendah 1% memiliki dasar yang valid berdasarkan teori pengambilan sampel Random Sampling. Metode Stratified Random Sampling dengan 2000 sampel menghasilkan Margin of Error di bawah 1% menurut percobaan dari Penulis. Dengan Margin of Error 1% maupun 2% tetap terdapat jarak lebih besar dari 2 kali Margin of Error untuk kedua metode, sehingga tidak bisa dipungkiri bahwa Quick Count dapat memprediksikan bahwa pasangan ’01’ menang.

Background

Mengapa saya menulis blog post ini? Hal ini saya lakukan karena sebagai alumni Universitas Indonesia, terketuk hati dengan pernyataan pak Ronnie Rusli yang mempertanyakan sampling dari juragan survey Indonesia. Saya dan teman juga skeptis tentang kehebatan juragan survey yang mampu menghasilkan data cukup akurat dengan hanya 2000 data dengan margin of error yang sebesar 1%.

Ramai di jagat media sosial bahwa mereka harus dipertemukan dengan suasana debat akademis. Namun, menurut saya kurang tepat jika hal ini diselesaikan dengan debat akademis, permasalahan ini adalah permasalahan matematika dan dengan mudah dapat diselesaikan dengan perhitungan. Banyak alat yang dapat digunakan pada zaman ini seperti pemrograman Python untuk melakukan simulasi hitungan tanpa perlu menggunakan berlembar-lembar kertas dan pulpen.

Seperti kata banyak orang, “Show, don’t tell“, maka saya akan menunjukkan simulasi perhitungan Quick Count dengan metode Stratified Random Sampling dengan Python. Anda pun dapat mengikuti apa yang saya kerjakan karena saya akan membuka datanya di GitHub saya: https://github.com/josefmtd/kpu-data/

Margin of Error dari Random Sampling

Untuk penjelasan lebih lanjut tentang Margin of Error dan Standard Error, Anda dapat melihat di blog oleh dosen Teknik Industri UI: Bapak Komarudin https://staff.blog.ui.ac.id/komarudin74/mengotak-atik-statistik-quick-count-pilpres-2014/

Seperti dari coretan bapak Ronnie Rusli, margin of error didefinisikan dengan:

\displaystyle SE = \frac{SD}{\sqrt{n}}

\displaystyle MOE = z(SE)

MOE adalah Margin of Error, SD adalah Standard Deviation, n adalah jumlah sample, dan z adalah nilai critical yang diambil dari tabel F(z) sesuai dengan nilai confidence level yang kita inginkan. Untuk confidence level 99%, kita menggunakan nilai z = 2.58.

Untuk mencari sample size dari sebuah data pilihan biner seperti Pemilu yakni antara 01 dan 02, maka dapat digunakan rumus Standard Error khusus untuk proporsi yakni:

\displaystyle SE_p = \sqrt{\frac{p(1-p)}{n}}

dengan menggabungkan rumus di atas dengan rumus MOE dan mengubahnya untuk mencari jumlah sampel (n) maka didapatkan persamaan:

\displaystyle n = \frac{z^2(p(1-p))}{(MOE)^2}

p adalah persentase suara 01 dan (1-p) merepresentasikan suara 02, karena sebelum sampling kita tidak bisa menebak persentase suara, kita dapat masukkan nilai 50% untuk p dan 50% untuk (1-p).

\displaystyle n = \frac{ (2.58)^2 (50\%) (50\%) }{(1\%)^2} = 16641 samples

\displaystyle n = \frac{ (2.58)^2 (50\%) (50\%) }{(0.5\%)^2} = 66564 samples

Dengan persamaan ini kita dapat membuktikan secara teori bahwa untuk menghasilkan data dengan Margin of Error sebesar 1% perlu 16641 sampel dan 0.5% perlu 66564 sampel. Sementara Quick Count Indikator dengan sampel 2975 disebut-sebut dapat menghasilkan Margin of Error sebesar 0.63%, dan Quick Count SMRC dengan sampel 5907 TPS dapat menghasilkan Margin of Error sebesar 0.5%. Hal ini tentu berkontradiksi dengan persamaan yang saya tunjukkan di atas. Saya dan teman saya, setuju dengan pak Ronnie Rusli yang skeptis dengan nilai sampel yang sangat sedikit dapat menghasilkan Margin of Error yang sangat rendah.

Namun, perlu digarisbawahi juga sikap skeptis ini berdasarkan teori statistik Random Sampling. Sikap skeptis ini bukan karena pendirian partisan, namun karena dasar ilmiah. Teman sayapun berbicara: “Always question your results“, maka saya mencoba melakukan Random Sampling dengan Python untuk membuktikan apakah benar lembaga survey dapat menghasilkan margin of error serendah itu dengan sampel 2000 TPS?

UPDATE 05 May: Bapak Doktor Ronnie Rusli mengeluarkan rumus yang digunakan untuk mendapatkan sampel:

\displaystyle n_{tps} = \frac{[p(1-p)]}{\displaystyle \frac{MOE}{z_{99\%}^2} + \frac{p(1-p)}{N}}

\displaystyle n_{tps} = \frac{25\%}{\displaystyle \frac{1\%}{2.58^2} + \frac{25\%}{809000}} = 166

Capture.PNG

Hasilnya malah melebihi jumlah TPS, sepertinya ada yang salah dengan rumus pak Doktor Ronnie Rusli, saya tetap akan mengambil dari rumus yang saya cantumkan, berasal dari catatan statistik saya dan juga sama dengan yang ditulis oleh bapak dosen Komarudin.  Terdapat typo dalam input kalkulator sehingga  hasil menjadi salah, persamaan Dr. Ronnie menghasilkan nilai 166 TPS

Menggunakan nilai sampel TPS dengan persamaan oleh Dr. Ronnie, hasilnya adalah 166 TPS, hasil ini malah lebih rendah dari 2000 TPS. Pasti ada kesalahan di persamaan

Intermezzo: Distribusi Suara Nasional

Keskeptisan saya tidak hanya berada pada jumlah sampel, tapi juga terhadap distribusi suara tiap-tiap TPS per provinsi, apakah datanya terdistribusi normal? Seperti yang sudah dibahas di blog bapak dosen FTUI di atas, penggunaan rumus-rumus dalam Quick Count dengan Random Sampling, menggunakan asumsi bahwa data terdistribusi normal, dengan Pilpres yang terlalu terpolarisasi seperti ini, apakah benar Pilpres menghasilkan data distribusi Normal? Saya ragu, sehingga saya mengambil data untuk mencari tahu.

Keraguan saya, saya luapkan dengan code sederhana dalam Python seperti ini (dapat diakses di github.com/josefmtd/kpu-data/distributionPlot.py):

Tekan Spoiler ini untuk menunjukkan code distributionPlot.py
# -*- coding: utf-8 -*-
"""
DistributionPlot.py
Created on Fri May 3 20:35:56 2019

@author: JosefStevanus
"""

import os
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

directory = 'C:/Users/JosefStevanus/Documents/GitHub/kpu-data/'
data = {}
cleanNolSatuNasional = []
cleanNolDuaNasional = []
cleanTotalSuaraNasional = []

for filename in os.listdir(directory):
if filename.endswith(".csv"):
dataName = filename[:-4]
data[dataName] = pd.read_csv(filename)

nolSatu = data[dataName]["01 KPU"] 
nolDua = data[dataName]["02 KPU"]
cleanNolSatu = []
cleanNolDua = []

for x in range(len(nolSatu)):
if nolSatu[x] == 0 and nolDua[x] == 0:
continue
else:
cleanNolSatu.append(nolSatu[x])
cleanNolDua.append(nolDua[x])

cleanTotalSuara = np.add(cleanNolSatu, cleanNolDua)

cleanNolSatuNasional += cleanNolSatu
cleanNolDuaNasional += cleanNolDua
cleanTotalSuaraNasional += cleanTotalSuara.tolist() 

percentageNolSatu = np.divide(cleanNolSatu, cleanTotalSuara)*100
percentageNolSatuSeries = pd.Series(percentageNolSatu,
name = "Distribusi Persentase Suara 01 di "+dataName )

plotDistribusi, seaborn = plt.subplots()
sns.distplot(percentageNolSatuSeries, kde=False, bins=100, ax=seaborn)
plotDistribusi.savefig('C:/Users/JosefStevanus/Documents/GitHub/kpu-data/'+dataName+".png")
continue
else:
continue

percentageNolSatuNasional = np.divide(cleanNolSatuNasional, cleanTotalSuaraNasional)*100
percentageNolSatuNasionalSeries = pd.Series(percentageNolSatuNasional,
name = "Distribusi Persentase Suara 01 Nasional")

plotDistribusiNasional, seabornNasional = plt.subplots()
sns.distplot(percentageNolSatuNasionalSeries, kde=False, bins=100, ax = seabornNasional)
plotDistribusiNasional.savefig("C:/Users/JosefStevanus/Documents/GitHub/kpu-data/Nasional.png")

 

Hasilnya saya mendapatkan distribusi suara 01 diseluruh TPS di Indonesia seperti pada Gambar 1. Grafik histogram pada Gambar 1 menunjukkan distribusi yang sama sekali tidak terlihat seperti distribusi normal. Hal ini dapat dimaklumkan karena banyaknya provinsi yang tidak memiliki distribusi normal, contohnya pada Gambar 2 yang menunjukkan distribusi persentase suara di provinsi yang sangat mengunggulkan salah satu pihak (01 atau 02), atau pada Gambar 3 dimana distribusi suara menunjukkan persebaran suara yang terpolarisasi, di mana ada dua nilai peak pada 01 maupun 02.

Nasional
Gambar 1. Distribusi Persentase Suara 01 di Seluruh TPS di Indonesia
heavilySkewed
Gambar 2. Distribusi Persentase Suara 01 di Seluruh TPS di Provinsi (atas kiri) Aceh, (atas kanan) Bali, (bawah kiri) NTT, (bawah kanan) Papua Barat
polarized.png
Gambar 3. Distribusi Persentase Suara 01 di Seluruh TPS di Provinsi (atas kiri) Sumatera Utara, (atas kanan) Maluku Utara, dan (bawah kiri) Kalimantan Barat

Data yang sangat tidak lazim ini menyebabkan saya semakin skeptis dengan metode Quick Count yang dilaksanakan, karena semakin skeptis, maka saya mulai menulis program singkat dengan Random Sampling dan Stratified Random Sampling.

Random Sampling

Rumus yang saya gunakan di awal adalah rumus untuk mencari jumlah sampel Random Sampling. maka program pertama yang saya buat adalah program Random Sampling menggunakan data sementara dari Situng KPU menggunakan program Python, data saya saya ambil oleh hasil crawling data oleh bapak Yohanda Mandala (https://twitter.com/jokidz90).  Data ini terakhir di update oleh beliau pada tanggal 3 Mei jam 08.06 pagi.

Tekan Spoiler ini untuk melihat code RandomSampling.py
# -*- coding: utf-8 -*-
"""
RandomSampling.py
Created on Sat May 4 09:45:12 2019

@author: DarwinHarianto
"""

import os
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import random

directory = 'C:/Users/JosefStevanus/Documents/GitHub/kpu-data/'
data = {}
cleanNolSatuNasional = []
cleanNolDuaNasional = []
cleanTotalSuaraNasional = []

for filename in os.listdir(directory):
if filename.endswith(".csv"):
dataName = filename[:-4]
data[dataName] = pd.read_csv(filename)

nolSatu = data[dataName]["01 KPU"] 
nolDua = data[dataName]["02 KPU"]
cleanNolSatu = []
cleanNolDua = []

for x in range(len(nolSatu)):
if nolSatu[x] == 0 and nolDua[x] == 0:
continue
else:
cleanNolSatu.append(nolSatu[x])
cleanNolDua.append(nolDua[x])

cleanTotalSuara = np.add(cleanNolSatu, cleanNolDua)

cleanNolSatuNasional += cleanNolSatu
cleanNolDuaNasional += cleanNolDua
cleanTotalSuaraNasional += cleanTotalSuara.tolist() 
continue
else:
continue

percentageNolSatuNasional = np.divide(cleanNolSatuNasional, cleanTotalSuaraNasional)*100
percentageNolSatuNasionalSeries = pd.Series(percentageNolSatuNasional,
name = "Distribusi Persentase Suara 01 Nasional")

plotDistribusiNasional, seabornNasional = plt.subplots()
sns.distplot(percentageNolSatuNasionalSeries, kde=False, bins=100, ax = seabornNasional)
plotDistribusiNasional.savefig("C:/Users/JosefStevanus/Documents/GitHub/kpu-data/Nasional.png")

randomSample = random.sample(range(len(cleanTotalSuaraNasional)), 2000)

nolSatuRandomSample = []
nolDuaRandomSample = []
totalSuaraRandomSample = []

for x in randomSample:
    nolSatuRandomSample.append(cleanNolSatuNasional[x])
    nolDuaRandomSample.append(cleanNolDuaNasional[x])
    totalSuaraRandomSample.append(cleanTotalSuaraNasional[x])

percentageNolSatuRandomSample = np.divide(nolSatuRandomSample, totalSuaraRandomSample)*100
percentageNolSatuRandomSampleSeries = pd.Series(percentageNolSatuRandomSample, 
                                                name = "Distribusi Persentase Suara 01 Random Sample")

meanNolSatu = np.sum(nolSatuRandomSample)/np.sum(totalSuaraRandomSample)*100
meanNolDua = 100 - meanNolSatu
standardError = np.std(percentageNolSatuRandomSample)/np.sqrt(len(randomSample))

meanNolSatuNasional = np.sum(cleanNolSatuNasional)/np.sum(cleanTotalSuaraNasional)*100
meanNolDuaNasional = 100 - meanNolSatuNasional
standardErrorNasional = np.std(percentageNolSatuNasional)/np.sqrt(len(cleanTotalSuaraNasional))

plotDistribusiNasionalRandomSample, ax = plt.subplots(2,1)
sns.distplot(percentageNolSatuRandomSampleSeries, kde=False, bins=100, ax = ax[0])
sns.distplot(percentageNolSatuNasionalSeries, kde=False, bins=100, ax = ax[1])
plotDistribusiNasionalRandomSample.suptitle('{:.2f}% vs {:.2f}% Real Count Sementara \n {:.2f}% vs {:.2f}% Random Sampling dengan MOE = +/-{:.2f}%'.format(meanNolSatuNasional, meanNolDuaNasional, meanNolSatu, meanNolDua, standardError*2.58))
plotDistribusiNasionalRandomSample.savefig('RandomSampling.png')

 

Dalam mengambil sampel random dari Data Seluruh TPS Nasional yang saya dapatkan, saya menggunakan fungsi random yang ada di Python. Saya cukup percaya bahwa komputer tidak memihak 01 maupun 02. Untuk meyakinkan Anda, saya juga menunjukkan distribusi sampel yang diambil oleh program dan dibandingkan dengan distribusi populasi (yakni dari Situng KPU). Selain itu program saya juga menghitung mean persentase 01 dan 02, dan mencari nilai standard error dan margin of error nya. Semua hasil ditunjukkan dalam 1 grafik yang menunjukkan distribusi dan nilai Random Sampling terhadap Real Count.

RandomSampling4
Gambar 4. Hasil Random Sampling 2000 TPS dari seluruh TPS di Indonesia sementara (456.135 TPS)

Karena menggunakan Python, saya dapat dengan bebas mengambil beberapa kali Random Sampling sesuka saya, dengan mengambil data yang berbeda-beda. Hasilnya dapat dilihat pada Gambar 5.

randomSamplingData.png
Gambar 5. Beberapa Hasil Random Sampling 2000 TPS

Gambar 5 memperkuat pernyataan bapak Ronnie Rusli bahwa 2000 TPS jelas-jelas tidak cukup untuk menghasilkan Margin of Error dibawah 1.5%, bisa saja saya minta program itu berjalan 1000 kali lagi, saya yakin hasilnya pasti tidak jauh berbeda dari 1.5%.

“Insanity is doing the same thing and expecting a different result”, kata Albert Einstein

Perlu digarisbawahi bahwa Quick Count dari berbagai lembaga survey ini menggunakan Stratified Clustered Random Sampling, di mana suara individu direpresentasikan dalam TPS, dan jumlah TPS yang diambil per provinsi disesuaikan dengan jumlah proporsi penduduknya, sehingga kembali saya mempertanyakan hasil Random Sampling ini dan membuat program Stratified Random Sampling

Stratified Random Sampling

Lembaga survey sudah membagikan laporan quick count mereka, di situ saya dapat melihat berapa jumlah TPS yang mereka pakai, dengan mengikuti proporsi yang mereka gunakan, saya mengambil sampel TPS berdasarkan proporsi jumlah TPS provinsi tersebut dibandingkan TPS seluruh Indonesia.

Mereka menuliskan rumus quick count untuk mendapatkan persentase nilai 01 atau 02 dan juga nilai margin of errornya. Berikut yang ditampilkan oleh lembaga survey tersebut:

\displaystyle \hat{p} = \frac{\displaystyle \sum_{h=1}^{H} \sum_{i=1}^{n_h} \frac{N_h}{n_h}y_{hi}}{\displaystyle \sum_{h=1}^{H} \sum_{i=1}^{n_h} \frac{N_h}{n_h}x_{hi}}

\displaystyle moe = 2(SE) = 2 \sqrt{\frac{1}{\hat{X}^2}\sum_{h=1}^{H} \frac{N_h(N_h - n_h)}{n_h} \hat{var_h}}

Kedua persamaan itu saya realisasikan dalam program Python sebagai berikut:

Tekan Spoiler ini untuk melihat code StratifiedRandomSampling.py
# -*- coding: utf-8 -*-
"""
StratifiedRandomSampling.py
Created on Sat May 4 09:45:12 2019

@author: JosefStevanus
"""

# -*- coding: utf-8 -*-
"""
Created on Fri May 3 20:35:56 2019

@author: JosefStevanus
"""

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import random

class dataPemilu:
def __init__(self, filename, dataSource, totalTPS, sampleSize):
self.totalTPS = totalTPS
self.sampleSize = sampleSize
self.nolSatu = []
self.nolDua = []
self.totalSuara = []
self.sampleNolSatu = []
self.sampleNolDua = []
self.sampleTotalSuara = []
self.nolSatuQuickCount = 0
self.nolDuaQuickCount = 0
self.totalSuaraQuickCount = 0
self.varianceQuickCount = 0
self.filename = filename
self.getRawData(dataSource)
self.cleanData()
self.getQuickCountData()

def getRawData(self, dataSource):
data = pd.read_csv(self.filename)
if dataSource == "KPU":
self.nolSatu = data["01 KPU"]
self.nolDua = data["02 KPU"]
elif dataSource == "KawalPemilu":
self.nolSatu = data ["01 KawalPemilu"]
self.nolDua = data ["02 KawalPemilu"]
else:
print("Source data does not exist")
self.totalSuara = np.add(self.nolSatu, self.nolDua)

def cleanData(self):
cleanNolSatu = []
cleanNolDua = []
for x in range (len(self.totalSuara)):
if self.nolSatu[x] == 0 and self.nolDua[x] == 0:
continue
else:
cleanNolSatu.append(self.nolSatu[x])
cleanNolDua.append(self.nolDua[x])

self.nolSatu = cleanNolSatu
self.nolDua = cleanNolDua
self.totalSuara = np.add(cleanNolSatu, cleanNolDua).tolist()

def randomSample(self):
listSample = random.sample(range(len(self.totalSuara)), self.sampleSize)
for x in listSample:
self.sampleNolSatu.append(self.nolSatu[x])
self.sampleNolDua.append(self.nolDua[x])
self.sampleTotalSuara.append(self.totalSuara[x])

def getQuickCountData(self):
self.randomSample()
self.nolSatuQuickCount = np.sum(self.sampleNolSatu)*self.totalTPS/self.sampleSize
self.nolDuaQuickCount = np.sum(self.sampleNolDua)*self.totalTPS/self.sampleSize
self.totalSuaraQuickCount = np.sum(self.sampleTotalSuara, dtype=np.float64)*self.totalTPS/self.sampleSize # needs 64 bit to prevent overflow

def getVariance(self, proporsi):
vhi = np.subtract(self.sampleNolSatu, np.multiply(proporsi, self.sampleTotalSuara))
vh = np.sum(vhi)/self.sampleSize
variance = np.sum(np.square(vhi-vh))/(self.sampleSize-1)
varianceQuickCount = variance*self.totalTPS*(self.totalTPS-self.sampleSize)/self.sampleSize
return varianceQuickCount

sampleSize = 2000

totalTPSAceh = 17389
totalTPSBali = 12386
totalTPSBanten = 33471
totalTPSBengkulu = 6165
totalTPSDaerahIstimewaYogyakarta = 11781
totalTPSDaerahKhususIbukotaJakarta = 29063
totalTPSGorontalo = 5866
totalTPSJambi = 11342
totalTPSJawaBarat = 70169
totalTPSJawaTengah = 86121
totalTPSJawaTimur = 67454
totalTPSKalimantanBarat = 10791
totalTPSKalimantanSelatan = 6722
totalTPSKalimantanTengah = 4780
totalTPSKalimantanTimur = 6780
totalTPSKalimantanUtara = 1512
totalTPSKepulauanBangka = 3597
totalTPSKepulauanRiau = 4659
totalTPSLampung = 20407
totalTPSMaluku = 2705
totalTPSMalukuUtara = 3805
totalTPSNusaTenggaraBarat = 10082
totalTPSNusaTenggaraTimur = 7656
totalTPSPapua = 1006
totalTPSPapuaBarat = 749
totalTPSRiau = 13174
totalTPSSulawesiBarat = 1857
totalTPSSulawesiSelatan = 14608
totalTPSSulawesiTengah = 3719
totalTPSSulawesiTenggara = 3710
totalTPSSulawesiUtara = 3986
totalTPSSumateraBarat = 13804
totalTPSSumateraSelatan = 19427
totalTPSSumateraUtara = 30883
totalTPSNasional = 809497

aceh = dataPemilu("Aceh.csv", "KPU", totalTPSAceh, int(1.95/100*sampleSize))
bali = dataPemilu("Bali.csv", "KPU", totalTPSBali, int(1.53/100*sampleSize))
banten = dataPemilu("Banten.csv", "KPU", totalTPSBanten, int(4.13/100*sampleSize))
bengkulu = dataPemilu("Bengkulu.csv", "KPU", totalTPSBengkulu, int(0.76/100*sampleSize))
daerahIstimewaYogyakarta = dataPemilu("DaerahIstimewaYogyakarta.csv", "KPU", totalTPSDaerahIstimewaYogyakarta, int(1.46/100*sampleSize))
daerahKhususIbukotaJakarta = dataPemilu("DaerahKhususIbukotaJakarta.csv", "KPU", totalTPSDaerahKhususIbukotaJakarta, int(3.58/100*sampleSize))
gorontalo = dataPemilu("Gorontalo.csv", "KPU", totalTPSGorontalo, int(0.42/100*sampleSize))
jambi = dataPemilu("Jambi.csv", "KPU", totalTPSJambi, int(1.40/100*sampleSize))
jawaBarat = dataPemilu("JawaBarat.csv", "KPU", totalTPSJawaBarat, int(17.05/100*sampleSize))
jawaTengah = dataPemilu("JawaTengah.csv", "KPU", totalTPSJawaTengah, int(14.25/100*sampleSize))
jawaTimur = dataPemilu("JawaTimur.csv", "KPU", totalTPSJawaTimur, int(16.06/100*sampleSize))
kalimantanBarat = dataPemilu("KalimantanBarat.csv", "KPU", totalTPSKalimantanBarat, int(2.04/100*sampleSize))
kalimantanSelatan = dataPemilu("KalimantanSelatan.csv", "KPU", totalTPSKalimantanSelatan, int(1.34/100*sampleSize))
kalimantanTengah = dataPemilu("KalimantanTengah.csv", "KPU", totalTPSKalimantanTengah, int(1.00/100*sampleSize))
kalimantanTimur = dataPemilu("KalimantanTimur.csv", "KPU", totalTPSKalimantanTimur, int(1.34/100*sampleSize))
kalimantanUtara = dataPemilu("KalimantanUtara.csv", "KPU", totalTPSKalimantanUtara, int(0.27/100*sampleSize))
kepulauanBangka = dataPemilu("KepulauanBangka.csv", "KPU", totalTPSKepulauanBangka, int(0.47/100*sampleSize))
kepulauanRiau = dataPemilu("KepulauanRiau.csv", "KPU", totalTPSKepulauanRiau, int(0.67/100*sampleSize))
lampung = dataPemilu("Lampung.csv", "KPU", totalTPSLampung, int(3.24/100*sampleSize))
maluku = dataPemilu("Maluku.csv", "KPU", totalTPSMaluku, int(0.68/100*sampleSize))
malukuUtara = dataPemilu("MalukuUtara.csv", "KPU", totalTPSMalukuUtara, int(0.47/100*sampleSize))
nusaTenggaraBarat = dataPemilu("NusaTenggaraBarat.csv", "KPU", totalTPSNusaTenggaraBarat, int(1.98/100*sampleSize))
nusaTenggaraTimur = dataPemilu("NusaTenggaraTimur.csv", "KPU", totalTPSNusaTenggaraTimur, int(1.85/100*sampleSize))
papua = dataPemilu("Papua.csv", "KPU", totalTPSPapua, int(1.88/100*sampleSize))
papuaBarat = dataPemilu("PapuaBarat.csv", "KPU", totalTPSPapuaBarat, int(0.48/100*sampleSize))
riau = dataPemilu("Riau.csv", "KPU", totalTPSRiau, int(2.18/100*sampleSize))
sulawesiBarat = dataPemilu("SulawesiBarat.csv", "KPU", totalTPSSulawesiBarat, int(0.48/100*sampleSize))
sulawesiSelatan = dataPemilu("SulawesiSelatan.csv", "KPU", totalTPSSulawesiSelatan, int(3.25/100*sampleSize))
sulawesiTengah = dataPemilu("SulawesiTengah.csv", "KPU", totalTPSSulawesiTengah, int(1.13/100*sampleSize))
sulawesiTenggara = dataPemilu("SulawesiTenggara.csv", "KPU", totalTPSSulawesiTenggara, int(0.97/100*sampleSize))
sulawesiUtara = dataPemilu("SulawesiUtara.csv", "KPU", totalTPSSulawesiUtara, int(0.97/100*sampleSize))
sumateraBarat = dataPemilu("SumateraBarat.csv", "KPU", totalTPSSumateraBarat, int(2.06/100*sampleSize))
sumateraSelatan = dataPemilu("SumateraSelatan.csv", "KPU", totalTPSSumateraSelatan, int(3.13/100*sampleSize))
sumateraUtara = dataPemilu("SumateraUtara.csv", "KPU", totalTPSSumateraUtara, int(5.27/100*sampleSize))

## Real Count KPU

nolSatuNasional = aceh.nolSatu + bali.nolSatu + banten.nolSatu + bengkulu.nolSatu + daerahIstimewaYogyakarta.nolSatu + daerahKhususIbukotaJakarta.nolSatu + gorontalo.nolSatu + jambi.nolSatu + jawaBarat.nolSatu + jawaTengah.nolSatu + jawaTimur.nolSatu + kalimantanBarat.nolSatu + kalimantanSelatan.nolSatu + kalimantanTengah.nolSatu + kalimantanTimur.nolSatu + kalimantanUtara.nolSatu + kepulauanBangka.nolSatu + kepulauanRiau.nolSatu + lampung.nolSatu + maluku.nolSatu + malukuUtara.nolSatu + nusaTenggaraBarat.nolSatu + nusaTenggaraTimur.nolSatu + papua.nolSatu + papuaBarat.nolSatu + riau.nolSatu + sulawesiBarat.nolSatu + sulawesiSelatan.nolSatu + sulawesiTengah.nolSatu + sulawesiTenggara.nolSatu + sulawesiUtara.nolSatu + sumateraBarat.nolSatu + sumateraSelatan.nolSatu + sumateraUtara.nolSatu
nolDuaNasional = aceh.nolDua + bali.nolDua + banten.nolDua + bengkulu.nolDua + daerahIstimewaYogyakarta.nolDua + daerahKhususIbukotaJakarta.nolDua + gorontalo.nolDua + jambi.nolDua + jawaBarat.nolDua + jawaTengah.nolDua + jawaTimur.nolDua + kalimantanBarat.nolDua + kalimantanSelatan.nolDua + kalimantanTengah.nolDua + kalimantanTimur.nolDua + kalimantanUtara.nolDua + kepulauanBangka.nolDua + kepulauanRiau.nolDua + lampung.nolDua + maluku.nolDua + malukuUtara.nolDua + nusaTenggaraBarat.nolDua + nusaTenggaraTimur.nolDua + papua.nolDua + papuaBarat.nolDua + riau.nolDua + sulawesiBarat.nolDua + sulawesiSelatan.nolDua + sulawesiTengah.nolDua + sulawesiTenggara.nolDua + sulawesiUtara.nolDua + sumateraBarat.nolDua + sumateraSelatan.nolDua + sumateraUtara.nolDua
totalSuaraNasional = aceh.totalSuara + bali.totalSuara + banten.totalSuara + bengkulu.totalSuara + daerahIstimewaYogyakarta.totalSuara + daerahKhususIbukotaJakarta.totalSuara + gorontalo.totalSuara + jambi.totalSuara + jawaBarat.totalSuara + jawaTengah.totalSuara + jawaTimur.totalSuara + kalimantanBarat.totalSuara + kalimantanSelatan.totalSuara + kalimantanTengah.totalSuara + kalimantanTimur.totalSuara + kalimantanUtara.totalSuara + kepulauanBangka.totalSuara + kepulauanRiau.totalSuara + lampung.totalSuara + maluku.totalSuara + malukuUtara.totalSuara + nusaTenggaraBarat.totalSuara + nusaTenggaraTimur.totalSuara + papua.totalSuara + papuaBarat.totalSuara + riau.totalSuara + sulawesiBarat.totalSuara + sulawesiSelatan.totalSuara + sulawesiTengah.totalSuara + sulawesiTenggara.totalSuara + sulawesiUtara.totalSuara + sumateraBarat.totalSuara + sumateraSelatan.totalSuara + sumateraUtara.totalSuara

persentaseNolSatuNasional = np.divide(nolSatuNasional, totalSuaraNasional)*100

meanNolSatuNasional = np.sum(nolSatuNasional)/np.sum(totalSuaraNasional)*100
meanNolDuaNasional = 100 - meanNolSatuNasional

## Stratified Random Sampling

nolSatuQuickCountNasional = aceh.nolSatuQuickCount + bali.nolSatuQuickCount + banten.nolSatuQuickCount + bengkulu.nolSatuQuickCount + daerahIstimewaYogyakarta.nolSatuQuickCount + daerahKhususIbukotaJakarta.nolSatuQuickCount + gorontalo.nolSatuQuickCount + jambi.nolSatuQuickCount + jawaBarat.nolSatuQuickCount + jawaTengah.nolSatuQuickCount + jawaTimur.nolSatuQuickCount + kalimantanBarat.nolSatuQuickCount + kalimantanSelatan.nolSatuQuickCount + kalimantanTengah.nolSatuQuickCount + kalimantanTimur.nolSatuQuickCount + kalimantanUtara.nolSatuQuickCount + kepulauanBangka.nolSatuQuickCount + kepulauanRiau.nolSatuQuickCount + lampung.nolSatuQuickCount + maluku.nolSatuQuickCount + malukuUtara.nolSatuQuickCount + nusaTenggaraBarat.nolSatuQuickCount + nusaTenggaraTimur.nolSatuQuickCount + papua.nolSatuQuickCount + papuaBarat.nolSatuQuickCount + riau.nolSatuQuickCount + sulawesiBarat.nolSatuQuickCount + sulawesiSelatan.nolSatuQuickCount + sulawesiTengah.nolSatuQuickCount + sulawesiTenggara.nolSatuQuickCount + sulawesiUtara.nolSatuQuickCount + sumateraBarat.nolSatuQuickCount + sumateraSelatan.nolSatuQuickCount + sumateraUtara.nolSatuQuickCount
nolDuaQuickCountNasional = aceh.nolDuaQuickCount + bali.nolDuaQuickCount + banten.nolDuaQuickCount + bengkulu.nolDuaQuickCount + daerahIstimewaYogyakarta.nolDuaQuickCount + daerahKhususIbukotaJakarta.nolDuaQuickCount + gorontalo.nolDuaQuickCount + jambi.nolDuaQuickCount + jawaBarat.nolDuaQuickCount + jawaTengah.nolDuaQuickCount + jawaTimur.nolDuaQuickCount + kalimantanBarat.nolDuaQuickCount + kalimantanSelatan.nolDuaQuickCount + kalimantanTengah.nolDuaQuickCount + kalimantanTimur.nolDuaQuickCount + kalimantanUtara.nolDuaQuickCount + kepulauanBangka.nolDuaQuickCount + kepulauanRiau.nolDuaQuickCount + lampung.nolDuaQuickCount + maluku.nolDuaQuickCount + malukuUtara.nolDuaQuickCount + nusaTenggaraBarat.nolDuaQuickCount + nusaTenggaraTimur.nolDuaQuickCount + papua.nolDuaQuickCount + papuaBarat.nolDuaQuickCount + riau.nolDuaQuickCount + sulawesiBarat.nolDuaQuickCount + sulawesiSelatan.nolDuaQuickCount + sulawesiTengah.nolDuaQuickCount + sulawesiTenggara.nolDuaQuickCount + sulawesiUtara.nolDuaQuickCount + sumateraBarat.nolDuaQuickCount + sumateraSelatan.nolDuaQuickCount + sumateraUtara.nolDuaQuickCount
totalSuaraQuickCountNasional = aceh.totalSuaraQuickCount + bali.totalSuaraQuickCount + banten.totalSuaraQuickCount + bengkulu.totalSuaraQuickCount + daerahIstimewaYogyakarta.totalSuaraQuickCount + daerahKhususIbukotaJakarta.totalSuaraQuickCount + gorontalo.totalSuaraQuickCount + jambi.totalSuaraQuickCount + jawaBarat.totalSuaraQuickCount + jawaTengah.totalSuaraQuickCount + jawaTimur.totalSuaraQuickCount + kalimantanBarat.totalSuaraQuickCount + kalimantanSelatan.totalSuaraQuickCount + kalimantanTengah.totalSuaraQuickCount + kalimantanTimur.totalSuaraQuickCount + kalimantanUtara.totalSuaraQuickCount + kepulauanBangka.totalSuaraQuickCount + kepulauanRiau.totalSuaraQuickCount + lampung.totalSuaraQuickCount + maluku.totalSuaraQuickCount + malukuUtara.totalSuaraQuickCount + nusaTenggaraBarat.totalSuaraQuickCount + nusaTenggaraTimur.totalSuaraQuickCount + papua.totalSuaraQuickCount + papuaBarat.totalSuaraQuickCount + riau.totalSuaraQuickCount + sulawesiBarat.totalSuaraQuickCount + sulawesiSelatan.totalSuaraQuickCount + sulawesiTengah.totalSuaraQuickCount + sulawesiTenggara.totalSuaraQuickCount + sulawesiUtara.totalSuaraQuickCount + sumateraBarat.totalSuaraQuickCount + sumateraSelatan.totalSuaraQuickCount + sumateraUtara.totalSuaraQuickCount

proporsiNolSatu = nolSatuQuickCountNasional/totalSuaraQuickCountNasional
proporsiNolDua = nolDuaQuickCountNasional/totalSuaraQuickCountNasional
persentaseNolSatu = proporsiNolSatu*100
persentaseNolDua = proporsiNolDua*100

print(proporsiNolSatu)
print(proporsiNolDua)

varianceNasional = aceh.getVariance(proporsiNolSatu) + bali.getVariance(proporsiNolSatu) + banten.getVariance(proporsiNolSatu) + bengkulu.getVariance(proporsiNolSatu) + daerahIstimewaYogyakarta.getVariance(proporsiNolSatu) + daerahKhususIbukotaJakarta.getVariance(proporsiNolSatu) + gorontalo.getVariance(proporsiNolSatu) + jambi.getVariance(proporsiNolSatu) + jawaBarat.getVariance(proporsiNolSatu) + jawaTengah.getVariance(proporsiNolSatu) + jawaTimur.getVariance(proporsiNolSatu) + kalimantanBarat.getVariance(proporsiNolSatu) + kalimantanSelatan.getVariance(proporsiNolSatu) + kalimantanTengah.getVariance(proporsiNolSatu) + kalimantanTimur.getVariance(proporsiNolSatu) + kalimantanUtara.getVariance(proporsiNolSatu) + kepulauanBangka.getVariance(proporsiNolSatu) + kepulauanRiau.getVariance(proporsiNolSatu) + lampung.getVariance(proporsiNolSatu) + maluku.getVariance(proporsiNolSatu) + malukuUtara.getVariance(proporsiNolSatu) + nusaTenggaraBarat.getVariance(proporsiNolSatu) + nusaTenggaraTimur.getVariance(proporsiNolSatu) + papua.getVariance(proporsiNolSatu) + papuaBarat.getVariance(proporsiNolSatu) + riau.getVariance(proporsiNolSatu) + sulawesiBarat.getVariance(proporsiNolSatu) + sulawesiSelatan.getVariance(proporsiNolSatu) + sulawesiTengah.getVariance(proporsiNolSatu) + sulawesiTenggara.getVariance(proporsiNolSatu) + sulawesiUtara.getVariance(proporsiNolSatu) + sumateraBarat.getVariance(proporsiNolSatu) + sumateraSelatan.getVariance(proporsiNolSatu) + sumateraUtara.getVariance(proporsiNolSatu)

standardErrorProporsi = np.sqrt(1/np.square(totalSuaraQuickCountNasional)*varianceNasional)
marginOfErrorProporsi = 2*standardErrorProporsi
marginOfErrorPercentage = marginOfErrorProporsi*100

sampleNolSatuNasional = aceh.sampleNolSatu + bali.sampleNolSatu + banten.sampleNolSatu + bengkulu.sampleNolSatu + daerahIstimewaYogyakarta.sampleNolSatu + daerahKhususIbukotaJakarta.sampleNolSatu + gorontalo.sampleNolSatu + jambi.sampleNolSatu + jawaBarat.sampleNolSatu + jawaTengah.sampleNolSatu + jawaTimur.sampleNolSatu + kalimantanBarat.sampleNolSatu + kalimantanSelatan.sampleNolSatu + kalimantanTengah.sampleNolSatu + kalimantanTimur.sampleNolSatu + kalimantanUtara.sampleNolSatu + kepulauanBangka.sampleNolSatu + kepulauanRiau.sampleNolSatu + lampung.sampleNolSatu + maluku.sampleNolSatu + malukuUtara.sampleNolSatu + nusaTenggaraBarat.sampleNolSatu + nusaTenggaraTimur.sampleNolSatu + papua.sampleNolSatu + papuaBarat.sampleNolSatu + riau.sampleNolSatu + sulawesiBarat.sampleNolSatu + sulawesiSelatan.sampleNolSatu + sulawesiTengah.sampleNolSatu + sulawesiTenggara.sampleNolSatu + sulawesiUtara.sampleNolSatu + sumateraBarat.sampleNolSatu + sumateraSelatan.sampleNolSatu + sumateraUtara.sampleNolSatu
sampleNolDuaNasional = aceh.sampleNolDua + bali.sampleNolDua + banten.sampleNolDua + bengkulu.sampleNolDua + daerahIstimewaYogyakarta.sampleNolDua + daerahKhususIbukotaJakarta.sampleNolDua + gorontalo.sampleNolDua + jambi.sampleNolDua + jawaBarat.sampleNolDua + jawaTengah.sampleNolDua + jawaTimur.sampleNolDua + kalimantanBarat.sampleNolDua + kalimantanSelatan.sampleNolDua + kalimantanTengah.sampleNolDua + kalimantanTimur.sampleNolDua + kalimantanUtara.sampleNolDua + kepulauanBangka.sampleNolDua + kepulauanRiau.sampleNolDua + lampung.sampleNolDua + maluku.sampleNolDua + malukuUtara.sampleNolDua + nusaTenggaraBarat.sampleNolDua + nusaTenggaraTimur.sampleNolDua + papua.sampleNolDua + papuaBarat.sampleNolDua + riau.sampleNolDua + sulawesiBarat.sampleNolDua + sulawesiSelatan.sampleNolDua + sulawesiTengah.sampleNolDua + sulawesiTenggara.sampleNolDua + sulawesiUtara.sampleNolDua + sumateraBarat.sampleNolDua + sumateraSelatan.sampleNolDua + sumateraUtara.sampleNolDua
sampleTotalSuaraNasional = aceh.sampleTotalSuara + bali.sampleTotalSuara + banten.sampleTotalSuara + bengkulu.sampleTotalSuara + daerahIstimewaYogyakarta.sampleTotalSuara + daerahKhususIbukotaJakarta.sampleTotalSuara + gorontalo.sampleTotalSuara + jambi.sampleTotalSuara + jawaBarat.sampleTotalSuara + jawaTengah.sampleTotalSuara + jawaTimur.sampleTotalSuara + kalimantanBarat.sampleTotalSuara + kalimantanSelatan.sampleTotalSuara + kalimantanTengah.sampleTotalSuara + kalimantanTimur.sampleTotalSuara + kalimantanUtara.sampleTotalSuara + kepulauanBangka.sampleTotalSuara + kepulauanRiau.sampleTotalSuara + lampung.sampleTotalSuara + maluku.sampleTotalSuara + malukuUtara.sampleTotalSuara + nusaTenggaraBarat.sampleTotalSuara + nusaTenggaraTimur.sampleTotalSuara + papua.sampleTotalSuara + papuaBarat.sampleTotalSuara + riau.sampleTotalSuara + sulawesiBarat.sampleTotalSuara + sulawesiSelatan.sampleTotalSuara + sulawesiTengah.sampleTotalSuara + sulawesiTenggara.sampleTotalSuara + sulawesiUtara.sampleTotalSuara + sumateraBarat.sampleTotalSuara + sumateraSelatan.sampleTotalSuara + sumateraUtara.sampleTotalSuara

persentaseSampleNolSatuNasional = np.divide(sampleNolSatuNasional, sampleTotalSuaraNasional)*100
persentaseSampleNolSatuNasionalSeries = pd.Series(persentaseSampleNolSatuNasional,
name = "Distribusi Persentase Suara 01 Stratified Random Sampling")

plotDistribusiNasionalStratifiedRandomSample, ax = plt.subplots(2,1)
sns.distplot(persentaseNolSatuNasional, kde=False, bins=100, ax = ax[0])
sns.distplot(persentaseSampleNolSatuNasionalSeries, kde=False, bins=100, ax = ax[1])
plotDistribusiNasionalStratifiedRandomSample.suptitle('{:.2f}% vs {:.2f}% Real Count Sementara \n {:.2f}% vs {:.2f}% Stratified Random Sampling dengan MOE = +/-{:.2f}%'.format(meanNolSatuNasional, meanNolDuaNasional, persentaseNolSatu, persentaseNolDua, marginOfErrorPercentage))

plotDistribusiNasionalStratifiedRandomSample.savefig("StratifiedRandomSample4.png")

 

Program Python ini juga menghasilkan plot distribusi yang membandingkan plot distribusi suara nasional seluruh TPS sementara dengan sampling yang dipakai. Selain itu pada grafik juga dibubuhkan hasil persentase quick count 01 maupun 02 dengan margin of error yang didapatkan dari rumus di atas Gambar 6 menunjukkan hasil stratified random sampling dengan margin of error dibawah 1% dengan jumlah sampel sebesar 2000.

StratifiedRandomSample4
Gambar 6. Stratified Random Sampling dari 2000 TPS dari seluruh TPS di Indonesia data sementara (456.135 TPS)

Seperti Random Sampling, saya juga menjalankan program ini 5 kali sehingga terdapat 5 hasil yang berbeda seperti dapat dilihat pada Gambar 7. Gambar 7 menunjukkan dari kelima data yang ditampilkan pada Gambar 6 maupun Gambar 7 memiliki Margin of Error pada sekitaran 0.95%, yakni di bawah 1%. Melihat hasil ini saya dapat memahami bahwa hasil Quick Count dengan 2000 TPS menggunakan metode Stratified Random Sampling itu bukan hal yang mustahil.

stratifiedRandomSamplingData
Gambar 7. Beberapa Hasil Stratified Random Sampling dengan Sampel 2000 TPS

Kesimpulan

Menggunakan metode Random Sampling, memang dibuktikan bahwa 2000 TPS tidak akan mencapai Margin of Error sebesar 1%, namun standard Quick Count menggunakan Stratified Random Sampling dan dengan metode ini, percobaan Penulis dalam melaksanakan simulasi Stratified Random Sampling dengan Python membuktikan bahwa Quick Count dari 2000 TPS dapat menghasilkan Margin of Error di bawah 1%

Margin of Error dari Random Sampling memerlukan data yang terdistribusi normal, sedangkan persamaan Margin of Error dari Stratified Random Sampling sudah dengan asumsi bahwa tidak secara persis terdistribusi normal (lihat Metodologi SMRC slide 9). Penggunaan persamaan Margin of Error dengan asumsi distribusi normal terhadap data persebaran suara yang tidak terdistribusi normal, menyebabkan adanya perbedaan pendapat jumlah sampel yang dibutuhkan.

Menggunakan metode Stratified Random Sampling maupun Random Sampling biasa seperti yang ditampilkan di artikel ini, tetap menghasilkan perbedaan suara 01 dan 02 yang cukup signifikan (> 2(moe)) sehingga kesimpulan dari Quick Count tetap sama, yakni pasangan 01 diprediksikan akan memenangi Pemilihan Presiden 2019.

 

EDITED 05 May: Menambahkan spoiler untuk menunjukkan code, sehingga bagi pembaca yang tidak ingin melihat code dapat melewati bagian code Python yang digunakan.

Bessel Function for FM Analysis

Artikel sebelumnya membahas FM sampai dengan persamaan yang umum digunakan untuk merepresentasikan FM jika sinyal baseband nya adalah sebuah sinyal sinusoidal. Sinyal FM memiliki hubungan yang nonlinear antara keluaran modulasi x_{FM}(t) dengan sinyal baseband m(t), sehingga sulit untuk menganalisis sinyal FM. Contohnya untuk menghitung bandwidth yang dibutuhkan oleh sinyal FM, digunakan sinyal sinusoidal dengan frekuensi tertinggi yang ada pada sinyal baseband.

Bessel Function and Identities

Fungsi Bessel adalah fungsi yang dipakai untuk menghitung side-band yang dihasilkan dari sebuah sinyal sinusoidal yang dimodulasi secara modulasi frekuensi pada sebuah sinyal pembawa. Fungsi Bessel adalah solusi y(x) dari persamaan diferensial Bessel:

\displaystyle x^2 \frac{d^2y}{dx^2}+x\frac{dy}{dx}+(x^2-\alpha^2)y = 0

Karena persamaan diferensial Bessel ini adalah persamaan differensial orde dua, maka terdapat dua solusi untuk persamaan tersebut, fungsi Bessel pertama (J_\alpha) dan fungsi Bessel kedua (Y_\alpha).

besselfunction
Gambar 1. Fungsi Bessel Pertama J_\alpha

Fungsi Bessel yang digunakan untuk FM adalah fungsi Bessel pertama (J_\alpha). Fungsi tersebut dapat dilihat pada Gambar 1, selain itu beberapa persamaan identitas dari fungsi Bessel adalah sebagai berikut:

\displaystyle \cos\big(z \sin(\theta)\big) = J_0(z) + 2\sum_{k=1}^{\infty}J_{2k}(z) \cos(2k\theta)

\displaystyle \sin\big(z \sin(\theta)\big) = 2\sum_{k=0}^{\infty} J_{2k+1}(z)sin\big((2k+1)\theta\big)

\displaystyle J_{-n}(z) = (-1)^nJ_n(z)

Bessel Function for FM Signal

Sinyal modulasi frekuensi dengan sinyal baseband sinusoidal dapat dituliskan dengan persamaan:

\displaystyle x_{FM}(t) = A_c \cos\big(2\pi f_ct+\beta \sin(2\pi f_mt)\big)

Untuk mendapatkan persamaan yang dapat direpresentasikan dengan fungsi Bessel, pertama gunakan identitas trigonometri untuk mendapatkan persamaan:

\displaystyle x_{FM}(t) = A_c \Big(\cos (2\pi f_ct)\cos\big(\beta \sin(2\pi f_mt)\big) - \sin(2\pi f_ct) \sin \big(\beta \sin(2\pi f_mt)\big)\Big)

Kita dapat mengambil bagian persamaan pertama: A_c\cos(2\pi f_ct)\cos(\beta \sin(2\pi f_mt)) dengan fungsi identitas Bessel menjadi:

\displaystyle A_c \cos(2\pi f_c t)\bigg(J_0(\beta) + 2\sum_{k=1}^{\infty} J_{2k}(z)\cos(2kf_mt)\bigg)

Menggunakan persamaan trigonometri 2\cos A\cos B = \cos (A-B) + \cos (A+B), didapatkan persamaan sebagai berikut:

\displaystyle A_c\Big(J_0(\beta)\cos(2\pi f_ct) + \sum_{k=1}^{\infty} J_{2k}(\beta)\big(\cos(2\pi(f_c - 2kf_m)t) + \cos(2\pi(f_c + 2kf_m)t)\big)\Big)

Mengetahui bahwa 2k adalah bilangan genap dan identitas Bessel J_{-n}(z) = (-1)^nJ_n(z) maka persamaan di atas dapat diubah menjadi:

\displaystyle A_c\sum_{n \in bilangan bulat genap} J_n(\beta)cos(2\pi (f_c+nf_m)t)

Kita dapat mengambil bagian kedua pada persamaan awal: A_c\sin(2\pi f_ct)\sin(\beta\sin(2\pi f_mt)) dengan fungsi identitas Bessel menjadi:

\displaystyle A_c\sin(2\pi f_ct)\bigg(2\sum_{k=0}^{\infty} J_{2k}(\beta)\sin((2k+1)f_mt)\bigg)

Dengan menggunakan persamaan trigonometri 2\sin A\sin B = \cos (A-B) - \cos(A+B), didapatkan persamaan sebagai berikut:

\displaystyle A_c\sum_{k=0}^{\infty} J_{2k+1}(\beta) \bigg(\cos(2\pi (f_c-(2k+1)f_m)t) - \cos(2\pi (f_c+(2k+1)f_m)t)\bigg)

Mengetahui bahwa 2k+1 adalah bilangan ganjil, maka identitas Bessel J_{-n}(z) = (-1)^nJ_n(z) dapat dipakai pada persamaan di atas untuk mendapatkan

\displaystyle -A_c\sum_{n \in bilangan bulat ganjil}J_n(\beta)\cos(2\pi(f_c+nf_m)t)

Menambahkan kedua bagian bilangan ganjil dan bilangan genap kita mendapatkan sinyal FM dengan fungsi Bessel sebagai berikut:

\displaystyle A_c \sum_{k=-\infty}^{\infty} J_k(\beta)\cos(2\pi (f_c+kf_m)t)

FM Signal in Frequency Domain

Jika kita melakukan transformasi Fourier pada fungsi di atas, maka kita akan mendapatkan fungsi:

\displaystyle \frac{A_c}{2} \sum_{k=-\infty}^{\infty} J_k(\beta)\Big(\delta (f-f_c-f_m)+\delta (f+f_c+f_m)\Big)

Screenshot from 2019-03-25 21.01.58
Gambar 2. Representasi sinyal FM pada domain frekuensi (Sumber: INFN)

Jumlah sideband dapat dihitung berdasarkan tabel Bessel pada Gambar 3, besaran sideband dapat dipakai untuk menghitung berapa besar bandwidth yang dibutuhkan oleh sebuah sinyal FM. Namun untuk mengaproksimasi bandwidth sinyal FM, persamaan Carson sering dipakai sebagai panduan mengukur bandwidth yang lebih praktis. Nilai bandwidth dapat diaproksimasi dengan persamaan:

\displaystyle BW_{FM} = 2(\beta + 1)f_m

Screenshot from 2019-03-25 21.21.48
Gambar 3. Tabel Bessel (Sumber: USNA)

Pendekatan bandwidth menggunakan persamaan Carson ini lebih praktis akibat sideband paling besar yang ada di tabel Bessel memiliki magnituda yang dapat diabaikan. Nilainya jauh lebih rendah daripada carrier, yakni di bawah -10dBc.

Pada artikel selanjutnya, pembahasan tentang FM akan berlanjut dengan demodulasi FM.

Installing GNU Radio and RTL-SDR on ElementaryOS

This is another GNU Radio and RTL2832U Software Defined Radio tutorial based on the article I did on Ubuntu 18.04. ElementaryOS is one of Ubuntu based operating system. The ElementaryOS is designed to be easy to use and the user interface is a lot like the macOS. This ElementaryOS GNU Radio installation tutorial is made for people who just started using Linux so they can transition painlessly to using Linux for RTL SDR and GNU Radio purposes.

Installing the GNU Radio and GNU Radio Companion

Installing GNU Radio and the GNU Radio companion is as easy on ElementaryOS as it is on Ubuntu 18.04, all we need is to access the gnuradio package from APT.

# apt install gnuradio

The package installation takes a while, after finishing the GNU Radio 3.7.11-10 is finally installed on the computer. You can access the GNU Radio Companion program via Terminal or via Application Launcher.

$ gnuradio-companion

Now, we can use GNU Radio and the GNU Radio Companion, the next step of this tutorial is to install the RTL SDR package to use with GNU Radio. This will allow us to receive the IQ data from RTL SDR and use the GNU Radio to process the data as we need.

Screenshot from 2019-03-20 20.36.12
Figure 1. GNU Radio Companion on ElementaryOS

GNU Radio Source

As we can see on the Terminal output of gnuradio installation, the rtl-sdr driver is already installed, the only thing needed to be installed is the GNU Radio Blocks for RTL-SDR via the gr-osmosdr package. The gr-osmosdr is already available in the APT in ElementaryOS.

# apt install gr-osmosdr

After installing the GNU OsmoSDR module, we can access the RTL-SDR source block on GNU Radio Companion under the Sources category.

Screenshot from 2019-03-20 20-59-14
Figure 2. RTL SDR Source on GNU Radio Companion

This concludes the tutorial on how to install GNU Radio Companion on ElementaryOS. Other Ubuntu based operating system should work the same, the APT can be used as a simple way to install the GNU Radio. However, it may not be the latest version, if you need the latest version always use the source and compile your own version by CMake.

Installing GNU Radio for Software Defined Radio on Ubuntu 18.04

As I mentioned previously on my Frequency Modulation Basics post, I would be taking a practical approach to explain the concept of Frequency Modulation. This tutorial explains how to install the GNU Radio on Ubuntu 18.04 alongside the GNU Radio Companion and the SDR dongle support.

Installing the GNU Radio and GNU Radio Companion

First, we can begin by installing the GNU radio package via APT, but check first if the version in the APT system is up to date. As of the writing of this article, GNU Radio on Ubuntu 18.04’s APT is on 3.7.11 and the current version is on 3.7.13.4. To simplify matters, I will use the Ubuntu 18.04’s APT version until I encounter a bug that will only be fixed on the latest version.

# apt-get install gnuradio

The package installation takes a while, after finishing the GNU Radio 3.7.11-10 is finally installed on the computer. You can access the GNU Radio Companion program via Terminal or via Application Launcher.

$ gnuradio-companion

Now, we can use GNU Radio and the GNU Radio Companion, the next step of this tutorial is to install the RTL SDR package to use with GNU Radio. This will allow us to receive the IQ data from RTL SDR and use the GNU Radio to process the data as we need.

Screenshot from 2019-03-20 09-20-40
Figure 1. GNU Radio Companion Window

Software Defined Radio GNU Radio Module

The Software Defined Radio that we will be using is the ubiquitous and inexpensive RTL2832U based DVB-T Dongles turned to Software Defined Radio. We can use the GNU Radio module developed by Osmocom. To build this GNU Radio block module, we need at least GNU Radio v3.7, which is already installed via the Ubuntu APT.

We can install the GNU Radio Osmocom SDR module from the git repository of osmocom or via the github mirror. Before installing the GNU Radio Source, we first must install the RTL-SDR driver created by Osmocom for RTL2832U-based Software Defined Radio dongles.

RTL2382U Driver

First, we have to install any dependencies and required packages before we can build our GNU Radio Osmocom SDR module. The required packages are cmake and build-essential to make sure we can compile the source and install the module. We also need the libusb-1.0-0-dev for the SDR.

# apt-get update
# apt-get install cmake build-essential libusb-1.0-0-dev

Then, we have to clone the repository, save the cloned repository in the desired place, for example we can put it on the Downloads folder

$ cd ~/Downloads
$ git clone https://github.com/osmocom/rtl-sdr.git

After cloning the repository, we can begin installing first by creating the build file and compiling the source via CMake. After compiling, we can install the rtl_sdr. Use the following commands on the Terminal:

$ mkdir build
$ cd build
$ cmake ../ -DINSTALL_UDEV_RULES=ON -DDETACH_KERNEL_DRIVER=ON
$ make
# make install
# ldconfig

Make sure to have both the build options INSTALL_UDEV_RULES=ON and DETACH_KERNEL_DRIVER=ON. UDEV_RULES is needed to access the dongle as non-root user, and DETACH_KERNEL_DRIVER will detach the default kernel driver for the RTL2832U DVB.

GNU Radio Source

After installing the RTL SDR driver, we need to install the GNU Radio Source module that is provided also by Osmocom. We can also install this automatically via APT-GET since we don’t need  to change any build configuration, use the following command to install the gr-osmosdr package:

# apt-get install gr-osmosdr

After installing, we can access the RTL SDR source on GNU Radio Companion menu under the Sources category.

Screenshot from 2019-03-20 10-58-43
Figure 2. RTL-SDR Source on GNU Radio Companion

That’s it for today’s tutorial, we will explore more about GNU Radio and RTL SDR when I have finished my Frequency Modulation basics series and we will demonstrate the analysis of a real narrow-band and wide-band FM wave on SDR + GNU Radio Companion.

Frequency Modulation Basics

Modulasi Frekuensi (FM) adalah metode pemancaran radio yang paling populer, ditemukan oleh Edwin Armstrong pada tahun 1933. Pemancaran radio FM menggunakan modulasi frekuensi pita lebar (wideband), yang mampu menghasilkan suara fidelitas tinggi melalui pancaran sinyal radio, menghasilkan kualitas suara yang lebih baik daripada teknologi utama pemancar radio pada zamannya: modulasi amplitudo (AM). Modulasi frekuensi pita lebar masih sering digunakan sampai sekarang, di zaman telepon selularpun, fitur penerima radio FM tetap ada. Selain modulasi frekuensi pita lebar, modulasi frekuensi pita sempit (narrowband) juga masih umum digunakan seperti untuk radio komunikasi dua arah atau untuk radio amatir. Artikel ini akan membahas dasar-dasar dari modulasi frekuensi. Artikel ini adalah bagian dari seri FM yang akan membahas teori dan pendekatan praktis dari modulasi FM.

Modulation Basics

Modulasi dalam konteks telekomunikasi adalah sebuah proses pengubahan sebuah gelombang pembawa (carrier waveform) untuk membawa informasi (berupa sinyal baseband atau sinyal pemodulasi). Modulasi frekuensi adalah salah satu dari tiga jenis modulasi analog, yaitu: modulasi amplitudo, modulasi frekuensi, dan modulasi fasa.

Namun, modulasi frekuensi dan modulasi fasa umumnya dapat disembut “modulasi sudut”, menyisakan hanya dua jenis modulasi analog: sudut dan amplitudo. Penyatuan frekuensi dan fasa sebagai modulasi sudut disebabkan fasa dan frekuensi yang berhubungan erat, dan hubungan antara fasa dan frekuensi sudut dapat didefinisikan dengan persamaan berikut:

\displaystyle \omega(t) = \frac{d\phi(t)}{dt}

di mana \omega(t) adalah frekuensi sudut dan \phi(t) adalah fasa.

Gelombang pembawa umumnya didefinisikan sebagai gelombang sinusoidal sehingga dapat direpresentasikan dengan persamaan berikut:

\displaystyle c(t) = A_c \cos(\phi_i(t)) = A_c \cos(\omega_ct)

di mana c(t) adalah nilai sesaat dari gelombang pembawa sinusoidal, A_c adalah amplitudo dari gelombang pembawa, \phi_i(t) adalah fasa sesaat dari gelombang pembawa, dan \omega_c adalah frekuensi sudut dari gelombang pembawa.

Frequency Modulation Waveform Generation

Modulasi frekuensi merupakan sebuah jenis modulasi sudut, sesuai dengan namanya, mengubah sudut dari sebuah gelombang pembawa (c(t)) untuk membawa informasi dari sinyal baseband (m(t)). Cara termudah untuk menghasilkan sinyal FM adalah dengan menggunakan Voltage Controlled Oscillator (VCO). Tegangan dari sinyal baseband m(t) digunakan untuk mengatur frekuensi dari keluaran VCO (keluaran gelombang FM).

direct_method
Gambar 1. Proses modulasi frekuensi (Sumber: TutorialsPoint)

Gelombang sinyal baseband mengatur frekuensi sesaat dari gelombang FM, jika sinyal baseband 0, frekuensi sesaat dari gelombang FM menjadi sama dengan frekuensi pembawa f_c, saat sinyal baseband bukan nol, frekuensi sesaat (f_i(t)) dari sinyal FM dapat diekspresikan dengan persamaan:

\displaystyle f_i(t) = f_c + K_{VCO} m(t)

di mana K_{VCO} adalah sensitivitas modulasi atau gain dari VCO yang memiliki satuan Hz/V, dan K_{VCO}m(t) adalah deviasi frekuensi sesaat.

Karena fasa dapat dihasilkan dari persamaan frekuensi sudut, nilai fasa sesaat dapat direpresentasikan dengan persamaan:

\displaystyle \theta_i(t) = \int_{0}^{t} \omega_i(\tau) d\tau = \omega_ct + 2\pi K_{VCO} \int_{0}^{t} m(\tau) d\tau

Dengan mendapatkan fasa sesaat, persamaan sinyal FM didapatkan dengan memasukkan nilai fasa sesaat pada persamaan gelombang pembawa, sehingga menghasilkan persamaan:

\displaystyle x_{FM}(t) = A_c \cos\bigg(\omega_ct + 2\pi K_{VCO}\int_{0}^{t} m(\tau) d\tau\bigg)

Sinusoidal Analysis of FM Wave

Secara matematis, untuk menganalisa sinyal FM, umumnya sinyal baseband yang digunakan adalah sinyal satu frekuensi, yaitu sinyal sinusoidal dengan persamaan sebagai berikut:

\displaystyle m(t) = A_m \cos(\omega_mt)

di mana A_m adalah amplitudo sinyal baseband dan \omega_m adalah frekuensi sudut dari sinyal baseband. Jika persamaan di atas disubstitusikan dengan persamaan sinyal FM, dapat dihasilkan persamaan:

\displaystyle x_{FM}(t) = A_c \cos\bigg(\omega_ct + 2\pi K_{VCO}\int_{0}^{t} A_m \cos(\omega_m\tau) d\tau\bigg)

\displaystyle x_{FM}(t) = A_c \cos\bigg(\omega_ct + \frac{K_{VCO}A_m}{f_m}sin(\omega_mt)\bigg)

Deviasi frekuensi maksimum (\Delta f) didapatkan dari sensitivitas modulasi atau gain VCO dan amplitudo dari sinyal baseband. Perbandingan antara deviasi frekuensi maksimum (\Delta f) dengan frekuensi sinyal baseband (f_m) adalah indeks modulasi, \beta . Sehingga, persamaan FM dapat diturunkan sebagai berikut:

\displaystyle x_{FM}(t) = A_c \cos \big(\omega_ct + \beta\sin(\omega_mt)\big)

Pada artikel selanjutnya, saya akan membahas fungsi Bessel dan pengunaannya untuk menganalisa sinyal FM.

Frequency Modulation Basics (English)

Frequency Modulation is the prominent method of radio broadcasting, invented by Edwin Armstrong in 1933. FM Broadcasting uses Wide-band FM to produce high-fidelity sound over broadcast radio, capable of better sound quality than the leading broadcasting technology at that time: Amplitude Modulation Radio. Wide-band FM is still used to this day as the most popular broadcasting radio scheme, FM radio receivers are available in our mobile phones. Narrow-band FM is also widely used for commercial two-way radio or for amateur radio purposes. In this article, we will talk about the basics of Frequency Modulation scheme.

Modulation in telecommunications is a process of varying a carrier waveform (or carrier signal) to carry an information (or known as baseband signal). Frequency Modulation is one of the three analog modulation schemes, three of them being:

  1. Amplitude Modulation
  2. Frequency Modulation
  3. Phase Modulation

Although the Frequency Modulation and Phase Modulation is usually referred as “Angle Modulation”, leaving only two kinds of analog modulation: angle and amplitude. This is because phase and frequency is tightly coupled, and the relationship of phase and angular frequency can be defined by the equation

\displaystyle \omega(t) = \frac{d\phi(t)}{dt}

A carrier frequency is usually defined as a sinusoidal wave, thus can be represented in the following equation

\displaystyle c(t) = A_c \cos(\omega_ct)

where c(t) is the instantaneous value of the carrier cosine wave, A_c is the amplitude of the carrier, and \omega_c is the carrier angular frequency

Frequency Modulation is a type of Angle Modulation, in which the name implies, changes the angle of the carrier waveform to contain the information of the baseband signal m(t). The simplest way to generate an FM signal is through a Voltage Controlled Oscillator. The baseband signal m(t) is used to control the output frequency of the VCO (the FM wave).

direct_method
Figure 1. Direct Wideband Frequency Modulation generation (Source: TutorialsPoint)

The baseband signal controls the frequency of the FM wave, when the baseband signal is zero, the frequency of the output signal is the same as the carrier frequency f_c when the baseband signal is nonzero, the instantaneous frequency of the output signal is expressed by

\displaystyle f_i(t) = f_c + K_{VCO} m(t)

where K_{VCO} is the modulation sensitivity of the VCO expressed in units of Hz/V, and K_{VCO}m(t) represents the instantaneous frequency deviation (\Delta f).

Since the phase and angular frequency relationship is defined previously, we can derive the instantaneous phase as follows:

\displaystyle \theta_i(t) = \int_{0}^{t} \omega_i(\tau) d\tau = \omega_ct + 2\pi K_{VCO} \int_{0}^{t} m(\tau) d\tau

With the instantaneous phase defined, we can write the FM output signal as follows:

\displaystyle x_{FM}(t) = A_c \cos\bigg(\omega_ct + 2\pi K_{VCO}\int_{0}^{t} m(\tau) d\tau\bigg)

Mathematically, to analyze the FM signal, a single tone message is usually used, by approximating the baseband signal as a sinusoidal wave shown below:

\displaystyle m(t) = A_m \cos(\omega_mt)

where A_m is the amplitude of the baseband signal and \omega_m is the angular frequency of the baseband signal. Substituting the sinusoidal baseband signal to the FM output signals, we find:

\displaystyle x_{FM}(t) = A_c \cos\bigg(\omega_ct + 2\pi K_{VCO}\int_{0}^{t} A_m \cos(\omega_m\tau) d\tau\bigg)

\displaystyle x_{FM}(t) = A_c \cos\bigg(\omega_ct + \frac{K_{VCO}A_m}{f_m}sin(\omega_mt)\bigg)

The peak frequency deviation (\Delta f) is obtained from the modulation sensitivity of the VCO and the amplitude of the baseband signal. The ratio of the peak frequency deviation (\Delta f) and the modulating frequency (f_m) is called the modulation index, \beta . Thus, we can see the usual FM equation as follows:

\displaystyle x_{FM}(t) = A_c \cos \big(\omega_ct + \beta\sin(\omega_mt)\big)

In the next article, I will be talking about the Bessel function and how to use them to analyze a Frequency Modulation wave.

Adding Custom Libraries in KiCad (English)

When designing a PCB Prototype, the components you want to use are often not found in your favorite CAD software’s library, which made you have to add the component on your own. This short article will discuss how to make a library with all those components. For example, I’m going to make a library to help designing my LoRa APRS project design which I am improving. I have a LoRa module named “HDP14A ver 1.2”, an unusual module compared to the popular ones such as HopeRF or Ai-Thinker. To create this module, I have to make my own library. I will show you have to make it in KiCad 5

img_20180819_211330

Picture 1. Physical Shape Of LoRa Module HPD14A ver 1.2

Create Project

Open KiCad and start by making a new Project using the ‘ctrl+N’ Shortcut. Give it a name and assign it to a folder of your choice. After opening that Project, click on ‘Symbol Library Editor’ as in Picture 2

Screenshot 2018-08-19 21.24.06
Picture 2. Symbol Library Editor Button

New Library

After opening the Symbol Library Editor, you can make a new library on your own. To make one, click on File > New Library. As an example, I will make a Library and named it with lora-aprs.lib. After clicking on Save, you can choose to save it as a library for this project only, or as a global library

 

Screenshot 2018-08-19 21.42.02
Picture 3. lora-aprs Library Project

Create a Component

After making a Library, the next process is to create the component. You can do that by clicking on ‘Create New Symbol’ button, or by going to Symbol > New Symbol. You will then be asked to choose what Library is going to be used to save this new component you will create. After selecting the appropriate Library, press the OK button

Screenshot 2018-08-19 22.29.13
Picture 4. Adding New Components For lora-aprs Library

Add Pins on Symbol

After creating the components and naming it (mine is HPD14A), the next process is to add pins on the symbol. You can do that by going to Place > Pin to add pins on a new component. Adding pins can be done as shown in Picture 5. You can add the pin orientation, number, and name of the said pin. In addition, you can also change the type of the pin connection if it is an Input, Output, Bidirectional, Power, etc. After successfully adding a pin, you can configure its position and add a square with Place > Rectangle to make it available to be used

Screenshot 2018-08-19 22.31.23
Picture 5. Adding Pins On A New Component

Picture 6 shows the completed result, a LoRa module symbol with 16 pins. The pins on the symbol are grouped according to each of its function. When you’re satisfied with the result, you can save it with Ctrl+S. This component will then be accessible for your project, so you can immediately use it for PCB design

Screenshot 2018-08-19 22.57.47
Picture 6. Example of LoRa Module HPD14A – 433 MHz

Have fun trying KiCad!

Adding Custom Libraries in KiCad

Saat merancang prototipe PCB, seringkali komponen yang Anda gunakan tidak terdapat library nya di software CAD favorit Anda, sehingga Anda perlu menambahkannya sendiri. Artikel singkat ini akan membahas bagaimana cara membuat library untuk komponen-komponen tersebut. Sebagai contoh saya akan membuat library untuk membantu merancang board dari project LoRa APRS yang sedang saya kembangkan. Saya memiliki modul LoRa dengan nama “HDP14A ver 1.2”, modul ini tidak lazim seperti model yang lebih populer seperti buatan HopeRF ataupun Ai-Thinker. Untuk mencoba menggunakan modul ini, saya perlu membuat library sendiri. Saya akan menunjukkan cara membuat symbol library di KiCad 5 untuk komponen ini.

img_20180819_211330
Gambar 1. Bentuk fisik modul LoRa HPD14A ver 1.2

Create Project

Buka program KiCad dan mulai dengan membuat sebuah Project baru dengan menggunakan shortcut Ctrl+N. Beri nama Project Anda dan letakkan di folder yang Anda inginkan. Setelah membuka Project tersebut, tekan tombol Symbol library editor seperti pada Gambar 2.

Screenshot 2018-08-19 21.24.06
Gambar 2. Tombol Symbol library editor

New Library

Setelah membuka program Symbol library editor Anda dapat membuat library baru. Cara membuat library baru adalah dengan menekan tombol FileNew Library. Sebagai contoh, saya membuat library dengan nama sesuai dengan nama project, yaitu lora-aprs.lib. Setelah menekan tombol Save, Anda dapat memilih untuk menyimpan library ini sebagai library untuk project ini saja atau sebagai library global.

Screenshot 2018-08-19 21.42.02
Gambar 3. Library proyek lora-aprs

Create a Component

Setelah berhasil membuat library, proses selanjutnya adalah membuat komponen, membuat komponen dapat dilakukan dengan menekan tombol Create New Symbol, atau melalui menu Symbol > New Symbol. Setelah itu Anda akan diminta untuk memilih library mana yang akan dipakai untuk menyimpan komponen baru yang akan Anda buat. Anda bisa memilih library yang telah dibuat dan menekan tombol OK.

Screenshot 2018-08-19 22.29.13
Gambar 4. Menambahkan komponen baru pada library lora-aprs

Add Pins on Symbol

Setelah membuat komponen dan menamakannya sesuai dengan nama modul yang saya pakai: HPD14A, proses selanjutnya adalah menambahkan pin pada simbol tersebut. Anda dapat menggunakan menu Place > Pin untuk mulai menambahkan pin pada komponen baru. Selain itu, Anda juga dapat menggunakan Place > Rectangle untuk membuat kotak pada simbol tersebut. Anda bisa mengatur simbol yang Anda buat dengan meletakkan posisi pin sesuai yang Anda inginkan.

Penambahan pin dapat dilihat seperti pada Gambar 5. Anda dapat memasukan orientasi pin, nomor pin, dan nama pin. Selain itu, Anda juga dapat mengganti jenis koneksi pin tersebut, apakah berupa Input, Output, Bidirectional, Power, dll. Setelah berhasil menambahkan pin, Anda dapat mengaturnya supaya memiliki posisi yang sesuai, dan menambahkan kotak dengan Place > Rectangle untuk menghasilkan simbol yang siap pakai.

Screenshot 2018-08-19 22.31.23
Gambar 5. Penambahan pin pada komponen baru

Gambar 6 menunjukkan hasil simbol yang sudah selesai, yakni simbol modul LoRa dengan 16 pin. Pin pada simbol dikelompokkan sesuai dengan fungsi masing-masing pin. Setelah Anda puas dengan bentuk dari simbol yang telah dibuat, Anda dapat menyimpannya dengan Ctrl + S. Komponen ini akan dapat diakses pada project Anda, sehingga Anda dapat memulai membuat desain PCB dengan modul ini.

Screenshot 2018-08-19 22.57.47
Gambar 6. Contoh simbol modul LoRa HPD14A – 433 MHz

Selamat mencoba KiCad!

Adding GitHub Libraries in KiCad (English)

KiCad has an open source library, which is one of the advantage of it. Every week, KiCad’s library is updated through GitHub from KiCad. This article is dedicated to show how to install additional libraries for KiCad, and adding third-party libraries such as Digi-Key or Sparkfun Electronics for example. Digi-Key and Sparkfun’s libraries can be accessed through the links below:

  1. SparkFun Library: https://github.com/sparkfun/SparkFun-KiCad-Libraries
  2. Digi-Key Library: https://github.com/digikey/digikey-kicad-library

As an example, this article will show how to add Sparkfun library on KiCad 5.0.0 for Ubuntu 18.04

Cloning the Library via GitHub

Library can be accessed through Github, where almost everyone can share their library and contribute to the open source library in GitHub. If there are new additions in the library, the version renewal using Git will be much easier and won’t take much hard disk space, as only the additional files will be downloaded and not the entire files. to download Sparkfun library in GitHub, make sure you have the git package and execute the command below:

$ git clone https://github.com/sparkfun/SparkFun-KiCad-Libraries.git

After you finish the download, you can see several folders which contained Library and other supplementary folders, and also additional files. The result of downloading the library is as shown in Picture 1

Screenshot from 2018-08-13 21-56-02
Picture 1. Result of cloning KiCad’s library repository from Sparkfun

Adding the Library in KiCad

After downloading the library, the next thing is to open KiCad program to add the downloaded library. But before that, make a new Project by using File > New > New Project

Access “Environment Variable Configuration” in Preferences > Configure Paths. Add new Environment Variable using the “Add” button, then give the name “KICAD_SPARKFUN_SYMBOL” and show the folder location using the “Browse” button.

Screenshot from 2018-08-13 22-13-56
Picture 2. Adding Environment Variable

After adding Environment Variable, open Eeschema (Schematic Editor) and open Preferences > Manage Symbol Libraries. Press “Browse Libraries…” and select all desired library before pressing open

Screenshot from 2018-08-13 22-22-33
Picture 3. Adding Library Symbol to Create Schematics

Open Pcbnew (PCB Layout Editor) and open Preferences > Manage Footprint Libraries. Then, press “Browse Libraries…” and select all .pretty folders which you want to add before pressing “OK”

Screenshot from 2018-08-13 22-38-44
Picture 4. Adding Library Footprint to create PCB Layout

That’s all about adding libraries to KiCad. Next time, I’ll talk about creating a simple PCB design using KiCad. Have fun trying KiCad!

Installing KiCad: Open Source PCB Designer Suite (English)

KiCad is an open-source software to create schematics, PCB layouts, and Gerber. KiCad can be used in Windows, Linux, and macOS, and licensed under GNU GPL v3. KiCad is made by Jean Pierre Charras, a professor in Grenoble University. KiCad is also sponsored by CERN and had several KiCad developments done under CERN employees. If you’re interested to develop KiCad, you can do it through CERN

I’m interested to try this application to do simple projects as a hobby. KiCad became the optimal choice because it is open source, which allows anyone to use it. Aside from that, KiCad also has a library which is updated weekly, accessible via GitHub. Adafruit which produces many modules and even development boards also used KiCad to create their products

Ubuntu Installation

I tried to install it using Ubuntu 18.04 this time, you can see the installation manual from this link: KiCad Ubuntu

I’ll explain how to install KiCad PCB v5 in Ubuntu 18.04. First, you need to add this ppa:

# add-apt-repository -y ppa:js-reynaud/kicad-5

(Ubuntu 16.04) If you’re using Ubuntu 16.04, use apt-get update before installing KiCad:

# apt-get update
# apt-get install kicad

You will download around 391MB using that command, and installing KiCad will take around 4,970MB disk space. After that’s done, you can open KiCad through a terminal or a launcher

Screenshot from 2018-08-09 19-56-37
Picture 1. The terminal after installing KiCad and its application display

I plan to start a series of articles using KiCad. I’ll talk about adding library through KiCad GitHub after this. Have fun trying KiCad!