LilyPond

LilyPondは、楽譜を作成するためのプログラムです。

どのように動作するのか見てみましょう。これが、ソースコードです。解読は容易でしょう。これを書くのに5分間程かかりました。

\version "2.18.2"
\header{
  title = "Sinfonie in g"
}

global = {
  \tempo "Molto Allegro"
  \time 2/2
  \key g \minor
}

violineOne = \new Voice \relative g''{
  \set Staff.instrumentName = #"Violin 1 "

  \clef "treble"
  r2 r4 ees8(\p d) d4 ees8( d) d4 ees8( d) d4( bes')
  r4 bes8( a) g4 g8( f) ees4 ees8( d) c4 c
  r4 d8( c)
}

violineTwo = \new Voice \relative g'{
  \set Staff.instrumentName = #"Violin 2 "

  \clef "treble"
  r2 r4 ees8(\p d) d4 ees8( d) d4 ees8( d) d4( bes')
  r4 bes8( a) g4 g8( f) ees4 ees8( d) c4 c
  r4 d8( c)
}

\score {
  \new StaffGroup <<
    \new Staff << \global \violineOne >>
    \new Staff << \global \violineTwo >>
  >>
  \layout { }
}

ソースファイルをセーブしてコンパイルして、pdfファイルを得ます。そうら、できた!

Raspberry PIとpigpioライブラリ(2)

シェルコマンドpigsが、GPIOを制御するために提供されています。

pi@raspberrypi:~/Zpigpio $ sudo pigpiod

pi@raspberrypi:~/Zpigpio $ pigs w 4 1
pi@raspberrypi:~/Zpigpio $ pigs r 4
1
pi@raspberrypi:~/Zpigpio $ pigs w 4 0
pi@raspberrypi:~/Zpigpio $ pigs r 4
0

Raspberry PIとpiscope

piscopeは、Raspberryのためのディジタル波形ビューアーです。それは、選択されたGPIOの状態(ハイかロー)を、ローカルでもしくはリモートで表示します。

Raspberry PIとカーネルビルド(2)

あなたはソースをたとえばLinuxプラットフォームの上でクロスコンパイルすることもできますが、仮想マシンを使った場合は、SDカードの取り扱いが少しだけ面倒です。

私は、X11関連とかの不要なパッケージを捨てることにより、ローカルビルドを続けることにしました。

pi@raspberrypi:~ $ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/root       5.8G  2.1G  3.4G  38% /

pi@raspberrypi:~/Zkernel/linux $ time make -j4 zImage modules dtbs
real	65m26.604s
user	235m23.420s
sys	11m3.260s

オプション-j4を用いるとCPU温度が結構高くなるので、デバイスを冷やすために小さなファンを用いました。

pi@raspberrypi:~ $ vcgencmd measure_temp
temp=60.5'C

そして、最後にこのようになります。

pi@raspberrypi:~ $ uname -a
Linux raspberrypi 4.9.30-v7+ #1 SMP Mon May 29 07:24:28 UTC 2017 armv7l GNU/Linux

pi@raspberrypi:~ $ cat /proc/version
Linux version 4.9.30-v7+ (pi@raspberrypi) (gcc version 4.9.2 (Raspbian 4.9.2-10) ) #1 SMP Mon May 29 07:24:28 UTC 2017

pi@raspberrypi:~ $ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/root       5.8G  3.6G  2.0G  66% /

Raspberry PIとカーネルビルド

最初に現在のSDカードのイメージを保存しておきます。

pi@raspberrypi:~ $ uname -a
Linux raspberrypi 4.9.25-v7+ #994 SMP Fri Apr 28 16:56:00 BST 2017 armv7l GNU/Linux

pi@raspberrypi:~ $ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/root       5.8G  4.3G  1.2G  79% /
devtmpfs        458M     0  458M   0% /dev
tmpfs           462M     0  462M   0% /dev/shm
tmpfs           462M  6.4M  456M   2% /run
tmpfs           5.0M  4.0K  5.0M   1% /run/lock
tmpfs           462M     0  462M   0% /sys/fs/cgroup
/dev/mmcblk0p6   65M   21M   45M  33% /boot
tmpfs            93M     0   93M   0% /run/user/1000
/dev/mmcblk0p5   30M  398K   28M   2% /media/pi/SETTINGS

Mac-mini:Zraspberry $ sudo dd if=/dev/rdisk4 bs=1m of=raspberry20170529.img
7695+0 records in
7695+0 records out
8068792320 bytes transferred in 397.093440 secs (20319631 bytes/sec)

Mac-mini:Zraspberry $ ls -l
total 15759360
-rw-r--r--  1 root  staff  8068792320 May 29 08:03 raspberry20170529.img

そして、ソースをビルドしますが、しかし・・。

pi@raspberrypi:~/Zkernel $ git clone --depth=1 https://github.com/raspberrypi/linux

pi@raspberrypi:~/Zkernel $ cd linux
pi@raspberrypi:~/Zkernel/linux $ KERNEL=kernel7
pi@raspberrypi:~/Zkernel/linux $ make bcm2709_defconfig
pi@raspberrypi:~/Zkernel/linux $ time make -j4 zImage modules dtbs
scripts/kconfig/conf  --silentoldconfig Kconfig

...fatal error: error writing to /tmp/cc4wpGOn.s: No space left on device...

pi@raspberrypi:~/Zkernel/linux $ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/root       5.8G  5.5G   32K 100% /

WebSDRのユーザーインターフェース

WebSDRのユーザーインターフェースを少しづつ改良しています。

BFOのAかBかを選択してからウォーターフォール画面上をクリックすることにより、希望信号に同調することができます。スライダーは、CWピッチを変更するためのものです。

Raspberry PiでWebSDR(4)

index.jsというプログラムはHTTPサーバとして動作し、 接続が要求された時にあなたのWebブラウザにindex.htmlファイルを送ります。

index.js

 16 var http  = require('http');
 17 var fs    = require('fs');
 18 var index = fs.readFileSync(__dirname + '/index.html');
 19 var app   = http.createServer(function(req, res) {
 20                 res.writeHead(200, {'Content-Type' : 'text/html'});
 21                 res.end(index);
 22               })
 23               .listen(3000);

index.htmlファイルは、ウォーターフォール画面を表示したりオーディオ信号を受信して再生するためのJavaScriptのコードを含んでいます。

index.html

<!doctype html>
<html>
<head>
<style>
* { margin: 0; padding: 0; box-sizing : border-box; }
body { font: 13px Helvetica, Arial; }
form { background: # 000; padding: 3px; position: fixed; bottom: 0; width: 100% ; }
form input { border: 0; padding: 10px; width: 80% ; margin-right : .5% ; }
form button { width: 15% ; background: rgb(143, 188, 143); border: none; padding: 10px; }
#messages{list-style-type : none; margin : 0; padding : 0; }
#messages li{padding : 5px 10px; }
#messages li : nth-child(odd){background : #eee; }
#messages {margin-bottom : 40px }
#messages {font-size : 2em }
#btn1 {position : fixed; top : 10px; left :  10px; font-size : 2em; background-color: darkseagreen }
#btn2 {position : fixed; top : 10px; left :  60px; font-size : 2em; background-color: darkseagreen }
#btn3 {position : fixed; top : 10px; left : 150px; font-size : 2em; background-color: cornsilk }
#btn4 {position : fixed; top : 10px; left : 230px; font-size : 2em; background-color: cornsilk }
#btn5 {position : fixed; top : 10px; left : 310px; font-size : 2em; background-color: paleturquoise }
#btn6 {position : fixed; top : 10px; left : 390px; font-size : 2em; background-color: paleturquoise }
#btn7 {position : fixed; top : 10px; left : 470px; font-size : 2em; background-color: thistle }
#btn8 {position : fixed; top : 10px; left : 550px; font-size : 2em; background-color: thistle }
#mycanvas0 {position : fixed; top :  70px; left :  10px; font-size : 2em }
#mycanvas1 {position : fixed; top :  90px; left :  10px; font-size : 2em }
#mycanvas2 {position : fixed; top : 620px; left :  10px; font-size : 2em }
</style>
</head>

<body>
    <script src="https://code.jquery.com/jquery-1.11.1.js"></script>
    <script src="https://code.createjs.com/createjs-2015.11.26.min.js"></script>
    <script src='/socket.io/socket.io.js'></script>

    <button id="btn1"><font size="5">CQ</font></button>
    <button id="btn2"><font size="5">QRZ?</font></button>
    <button id="btn3"><font size="5">VFO+</font></button>
    <button id="btn4"><font size="5">VFO-</font></button>
    <button id="btn5"><font size="5">BFO+</font></button>
    <button id="btn6"><font size="5">BFO-</font></button>
    <button id="btn7"><font size="5">BFO+</font></button>
    <button id="btn8"><font size="5">BFO-</font></button>

    <div id="messages"></div>
    <form action="">
      <input id="m" autocomplete="off"/><button>Send</button>
    </form>
    <canvas id="mycanvas0" width="512" height= "20" style="background:white;"></canvas>
    <canvas id="mycanvas1" width="512" height="512" style="background:white;"></canvas>
    <canvas id="mycanvas2" width="280" height= "60" style="background:cornsilk;"></canvas>

<script>

function colormap(charcode) {  // 0x00~0xff
  var tmp = charcode / 255.0;  // 0.0~1.0
  var val;
  var r, g, b;
  if (tmp < 0.50) {
    r = 0.0;
  } else if (tmp > 0.75) {
    r = 1.0;
  } else {
    r = 4.0 * tmp - 2.0;
  }

  if (tmp < 0.25) {
    g = 4.0 * tmp;
  } else if (tmp > 0.75) {
    g = -4.0 * tmp + 4.0;
  } else {
    g = 1.0;
  }

  if (tmp < 0.25) {
    b = 1.0;
  } else if (tmp > 0.50) {
    b = 0.0;
  } else {
    b = -4.0 * tmp + 2.0;
  }

  rgb[1] = 255.0 * g;
  rgb[0] = 255.0 * r;
  rgb[2] = 255.0 * b;
}

function waterFall(myarray) {
  ctx1.putImageData(imgData1, 0, 1);
  imgData1 = ctx1.getImageData(0, 0, 512, 512);
  for (j = 0; j < 512; j++) {
    colormap(myarray[j]);
    imgData1.data[0 + j * 4] = rgb[0];
    imgData1.data[1 + j * 4] = rgb[1];
    imgData1.data[2 + j * 4] = rgb[2];
    imgData1.data[3 + j * 4] = 255;
  }
}

function showBpf(ctx0, imgData0, bfo) {
  var cwmarker0 = Math.floor( bfo[0]  / (16000 / 2048) );
  var cwmarker1 = Math.floor( bfo[1]  / (16000 / 2048) );
  console.log('bfo = ', bfo);
  
  for (i = 0; i < 20; i++) {
    for (j = 0; j < 512; j++) {
      var r, g, b, a;
      if(j == cwmarker0 || j == cwmarker1) {
        r = 0; g = 0; b = 0;
      } else if ( j >= cwmarker0-20 && j <= cwmarker0+20 && i > 10) {
        r = 135; g = 206; b= 235;
      } else if ( j >= cwmarker1-20 && j <= cwmarker1+20 && i > 10) {
        r = 216; g = 191; b= 216;
      } else {
        r = 255; g = 255; b= 255;
      }
      imgData0.data[0 + j * 4 + i * imgData0.width * 4] =   r;
      imgData0.data[1 + j * 4 + i * imgData0.width * 4] =   g;
      imgData0.data[2 + j * 4 + i * imgData0.width * 4] =   b;
      imgData0.data[3 + j * 4 + i * imgData0.width * 4] = 255;
    }
  }
  ctx0.putImageData(imgData0, 0, 0);
}

function onClick(e) {
  var rect = e.target.getBoundingClientRect();
  var mx   = e.clientX - rect.left;
  var my   = e.clientY - rect.top;
  console.log('mouse clicked: ', mx, my);
  socket.emit('message77', mx);
}

function playAudioStream(ctx, audio_f32) {
    var audio_buf    = ctx.createBuffer(1, audio_f32.length, 16000);
    var audio_src    = ctx.createBufferSource();
    var current_time = ctx.currentTime;

    audio_buf.getChannelData(0).set(audio_f32);

    audio_src.buffer = audio_buf;
    audio_src.connect(ctx.destination);

    if (current_time < scheduled_time) {
        audio_src.start(scheduled_time);
        scheduled_time += audio_buf.duration;
    } else {
        audio_src.start(current_time);
        scheduled_time = current_time + audio_buf.duration;
    }
}

// main

var socket  = io();

var myarray = new Array();
var canvas0, canvas1, canvas2;
var ctx0, ctx1;
var imgData0, imgData1;
var rgb = new Array(3);

canvas0 = document.getElementById('mycanvas0');
canvas1 = document.getElementById('mycanvas1');
ctx0    = canvas0.getContext('2d');
ctx1    = canvas1.getContext('2d');

canvas1.addEventListener('click', onClick, false);

imgData0 = ctx0.createImageData(512,  20);
imgData1 = ctx1.createImageData(512, 512);

var stage = new createjs.Stage("mycanvas2");
var t     = new createjs.Text("IC-7410", "26px serif", "DarkRed");
t.x       = 10;
t.y       = 10;
stage.addChild(t);
stage.update();

var bfo      = [1000.0, 2000.0];
showBpf(ctx0, imgData0, bfo);

for (i = 0; i < 512; i++) {
  for (j = 0; j < 512; j++) {
    imgData1.data[0 + j * 4 + i * imgData1.width * 4] = j % 256;
    imgData1.data[1 + j * 4 + i * imgData1.width * 4] = i % 256;
    imgData1.data[2 + j * 4 + i * imgData1.width * 4] = 128;
    imgData1.data[3 + j * 4 + i * imgData1.width * 4] = 255;
  }
}
ctx1.putImageData(imgData1, 0, 0);

var audioCtx = new AudioContext;
var scheduled_time = 0;

socket.on('your message', function(msg) {
  $('#messages').append($('<div>').text(msg));
  window.scrollTo(0, document.body.scrollHeight);
});

socket.on('waterfall', function(data) {
  waterFall(data);
});

socket.on('sound'    , function(data) {
  playAudioStream(audioCtx, new Float32Array(data));
});

socket.on('freqmsg', function(msg) {
  t.text = msg;
  stage.update();
});

$('#btn1').click(function() { socket.emit('message1'); console.log('message1'); });
$('#btn2').click(function() { socket.emit('message2'); console.log('message2'); });
$('#btn3').click(function() { socket.emit('message3'); console.log('message3'); });
$('#btn4').click(function() { socket.emit('message4'); console.log('message4'); });
$('#btn5').click(function() { socket.emit('message5'); bfo[0]+=100; showBpf(ctx0, imgData0, bfo);});
$('#btn6').click(function() { socket.emit('message6'); bfo[0]-=100; showBpf(ctx0, imgData0, bfo);});
$('#btn7').click(function() { socket.emit('message7'); bfo[1]+=100; showBpf(ctx0, imgData0, bfo);});
$('#btn8').click(function() { socket.emit('message8'); bfo[1]-=100; showBpf(ctx0, imgData0, bfo);});

$('form').submit(function() {
  socket.emit('your message', $('#m').val());
  $('#m').val('');
  return false;
});
</script>
</body>
</html>