Notes
Technology and OpenSource Notes

use Human::Brain

Fri, 13 Dec 2013

Christmas

permanent link

Fri, 29 Nov 2013

OpenSIPS Extras!
Two things in this post. Assuming you run the following code:

mpath="/usr/lib/opensips/modules"
loadmodule "statistics.so"
loadmodule "httpd.so"
loadmodule "mi_json.so"

timer_route[log_uptime, 7] {
  xlog("uptime = $stat(timestamp) , startup = $TS , current = $Ts\n");
}
through /usr/sbin/opensips -D -E -f opensips.cfg (doesn't need to be as root, you can run this under your user id), you will get:

permanent link

git + ctags + coffee-script
I used the `.git_template` ctags scripts from http://tbaggery.com/2011/08/08/effortless-ctags-with-git.html and combined with ctags definitions for CoffeeScript (to get a more generic version than CoffeTags), for example https://gist.github.com/shimaore/7692246 Now I can use ctrl-] and :ta.

permanent link

Thu, 31 Oct 2013

gdebi debian/control

gdebi debian/control
will intall the build dependencies for a source Debian package.

permanent link

Mon, 28 Oct 2013

PouchDB Calendar
Writing a client-side application with PouchDB was my personal challenge last week; I committed PouchDB Calendar. View it or get the code.

permanent link

Wed, 23 Oct 2013

mk-build-deps!

 mk-build-deps -i -s sudo 

permanent link

Wed, 11 Sep 2013

Rebuild a Debian Kernel
Basically use the instructions on https://wiki.debian.org/HowToRebuildAnOfficialDebianKernelPackage Some notes:

fakeroot make -f debian/rules.gen setup_amd64_none_amd64
fakeroot make -f debian/rules.gen binary-arch_amd64_none_amd64

permanent link

Fri, 02 Aug 2013

Jack and PulseAudio

I want Jack as the main interface to the hardware, but PulseAudio is supported by many applications (like 0ad). I finally got things to work; essentially I start jack as usual (using qjackctl at session startup), then I needed to tweak Pulse to only use jack.

The configuration files for Pulse are in ~/.config/pulse; I ended up reading the manual pages for pulse-client.conf, pulse-server.conf, etc. I copied client.conf, daemon.conf, and default.pa from /etc/pulse, then:

The definitive trick is the removal of the udev module. Also module-jackdbus-detect.so doesn't do what you think it would.

Tags: audio, jack, pulseaudio

permanent link

Mon, 17 Jun 2013

UmTRX channel on geeknode
Pour les utilisateurs francophone de l'UmTRX, #umtrx sur geeknode.net

permanent link

Sun, 26 May 2013

BSC-MSC interface specs
ETSI 48.002 overview 48.006 DTAP (MS-MSC), part of BSSAP; also MTP/M3UA-lite for BSSAP (SCCP class 0 and class 2) 48.008 BSSMAP (BSS-MSC), part of BSSAP TS 52 series for BSSOMAP (runs over SCCP) Also TS 29.002 M3UA Q.711 SCCP TS 04.xxx DTAP RFC3332 M3UA

permanent link

Thu, 09 May 2013

Snippets for fs_cli
Remove calls matching a date range:

fs_cli -p CCNQ -x 'show calls' | awk -F, '{ print $3,$1}' | egrep '^2013-05-0[1-8]' | awk '{print "uuid_kill " $3}' | xargs -l -d '\n' fs_cli -p CCNQ -x

permanent link

Tue, 23 Apr 2013

ZappaJS as a generic web server: CGI

Trying to write a tiny web server for munin:

#!/usr/bin/env coffee
cgi = require 'cgi'
require('zappajs') 8080, ->
  @get '/munin-cgi/munin-cgi-graph*', cgi '/usr/lib/munin/cgi/munin-cgi-graph', mountPoint: '/munin-cgi/munin-cgi-graph'
  @use static: '/var/cache/munin/www'
This uses the cgi package and a static mount point from within zappajs. Start it with:
sudo -u www-data daemon -n server /path/to/server.coffee

permanent link

Mon, 25 Mar 2013

Gangnam Style Generation
By now there's an entire generation (of 5-to-11-year old, I'd say) whose first dance moves has been the horse dance from Gangnam Style, and whose first sing-along has been "hey sexy lady".

permanent link

Tue, 05 Mar 2013

UmTRXv2 (beta!)

UmTRXv2

Installation notes

posted: 2013-02-23 21:21

permanent link

Sat, 23 Feb 2013

Delaying outbound ACK

# Match customer (destination) based on IP address
iptables -t mangle -F POSTROUTING
iptables -t mangle -A POSTROUTING -o eth0 -p udp -d 198.51.100.33 -j CUSTOMER

# Create table to match "ACK sip" and set a mark.
iptables -t mangle -X CUSTOMER
iptables -t mangle -N CUSTOMER
iptables -t mangle -F CUSTOMER
iptables -t mangle -A CUSTOMER -m string --algo kmp --from 20 --to 30 --string 'ACK sip' -j MARK --set-mark 4242

# Change the default qdisc to use `prio` so that we can apply sub-qdisc
tc qdisc add dev eth0 root handle 1: prio
# The sub-qdisc will delay 250ms
tc qdisc add dev eth0 parent 1:3 handle 30: netem delay 250ms
# Match on the mark and send to the sub-qdisc
tc filter add dev eth0 protocol ip parent 1:0 prio 3 handle 4242 fw flowid 1:3
To check this is working:
tc -s -d qdisc dev eth0
iptables -L POSTROUTING -t mangle --verbose ; iptables -L CUSTOMER -t mangle --verbose

permanent link

Sat, 16 Feb 2013

Build FreeSwitch .deb
Recent versions (especially from git) may have non-working instructions. This seems to work:

./debian/bootstrap.sh
dpkg-buildpackage -b

permanent link

Mon, 03 Dec 2012

projectM
Built-in functions: int(x), abs, sin, cos, tan, asin, acos, atan, sqr(x), sqrt, pow(x,y), exp, log, log10, sign(x), min(x,y), max(x,y), sigmoid(x,y), atan2, rand(n>0), band(n,m), bor(n,m), bnot(n), if(n,x,y), equal(x,y), above(x,y), below(x,y), nchoosek(n,m), fact(n), print(...)
Built-in parameters:

float frating
float fwavescale
float gamma, fGammaAdj
float echo_zoom, fVideoEchoZoom
float echo_alpha, fvideoechoalpha
float wave_a, fwavealpha
float fwavesmoothing
float fmodwavealphastart
float fmodwavealphaend
float fWarpAnimSpeed
float fWarpScale
(float warp)
float fshader
float decay, fdecay
int echo_orient, nVideoEchoOrientation
int wave_mode, nwavemode
bool wave_additive, bAdditiveWaves
bool bmodwavealphabyvolume
bool wave_brighten, bMaximizeWaveColor
bool wrap, btexwrap
bool darken_center, bdarkencenter
bool bredbluestereo
bool brighten, bbrighten
bool darken, bdarken
bool solarize, bsolarize
bool invert, binvert
bool bmotionvectorson
bool wave_dots, bwavedots
bool wave_thick, bwavethick
Per-pixel:
float warp
float zoom
float rot
float zoomexp, fzoomexponent
float cx
float cy
float dx
float dy
float sx
float sy
Blur:
float b1n
float b2n
float b3n
float b1x
float b2x
float b3x
float b1ed
Wave:
float wave_r
float wave_g
float wave_b
float wave_x
float wave_y
float wave_mystery, fWaveParam
Border (inner, outer):
float ob_size
float ob_r
float ob_g
float ob_b
float ob_a
float ib_size
float ib_r
float ib_g
float ib_b
float ib_a
Motion Vectors:
float mv_r
float mv_g
float mv_b
float mv_x, nmotionvectorsx
float mv_y, nmotionvectorsy
float mv_l
float mv_dy
float mv_dx
float mv_a
Readonly builtin params:
float time
float bass
float mid
float treb
float bass_att
float mid_att
float treb_att
int frame
float progress
int fps

int meshx
int meshy
Per-Pixel Readonly:
float x
float y
float ang
float rad
Q-Variables (read-only):
q1 .. q32
Equations:
per_frame_
per_pixel_
per_frame_init_
wavecode_
wave_
per_point
per_frame
shapecode_
warp_
comp_
shape_
init
posted: 2012-12-03 22:22

permanent link

Sat, 10 Nov 2012

Firefox Add-Ons I use
For development: Acebug, Cookies Manager+, dotjs, Firebug, JSONView, NoScript, RESTClient For general browsing: Adblock Plus, Element Hiding Helper for Adblock Plus, NoScript, QrCodeR, ScrapBook. And a couple dictionaries. posted: 2012-11-10 10:59

permanent link

Sun, 28 Oct 2012

Using local Node binaries

npm allows you to add binaries in the system paths by using the -g option. The installation typically looks like this:

sudo npm -g install coffee-script

However this means you need to: (a) be able to access root, or any other permission level to install global binaries; (b) run the same binaries as anybody else on your system.

A simple way to alleviate the problem is to install the npm packages you need from your home directory, which requires no specific priviledges, and add the proper directory to your path.

The first step is to modify your login shell so that it adds ~/node_modules/.bin to your path; for example using bash, add the following in your ~/.profile:

PATH="$HOME/node_modules/.bin:$PATH"

Now, whenever you install a package from your home directory, its binaries will show up in your path, and you will be able to execute them directly.

cd
npm install coffee-script
coffee -v
which coffee

As a system administrator this also means the only npm application you'll need to install is npm itself; users (esp. developers) can install in their private space all the applications they need.

posted: 2012-10-28 14:40

permanent link

Fri, 05 Oct 2012

Do as I want
Firefox about:config, keyword.enabled set to false, browser.fixup.alternate.enabled set to false. posted: 2012-10-05 19:51

permanent link

Mon, 05 Mar 2012

FreeSwitch en français
Création du channel #freeswitch sur geeknode.net. posted: 2012-03-05 10:03 tags: freeswitch

permanent link

Mon, 06 Feb 2012

Reverse proxy with Zappa
Zappa is a Node.js / Express.js web server and framework based on the ideas of Sinatra.

One of the many uses I have had for Zappa includes mixing dynamic content and reverse proxying other Zappa processes and CouchDB. The main tool here is the request module and a recent (0.6+) version of Node.js.

Here a short example (in CoffeeScript):

require('zappajs').run, ->

  request = require 'request'

  make_proxy = (proxy_base) ->
    return ->
      proxy = request
        uri: proxy_base + @request.url
        method: @request.method
        headers: @request.headers
        jar: false
        followRedirect: false
        timeout: 1000
      @request.pipe proxy
      proxy.pipe @response
      return

  couchdb_proxy = make_proxy 'http://127.0.0.1:5984'
  couchdb_urls = /^\/(_session|_users|_uuids|_utils|provisioning|billing|cdr|u.*)($|\/)/
  @get  couchdb_urls, couchdb_proxy
  @post couchdb_urls, couchdb_proxy
  @put  couchdb_urls, couchdb_proxy
  @del  couchdb_urls, couchdb_proxy

The make_proxy function is a generic reverse proxy for Zappa, and since there isn't really anything specific to Zappa here, can probably be used in Express.js as well. posted: 2012-02-06 10:14 edit: 2013-04-04: should not follow redirects. tags: zappa

permanent link

Thu, 10 Nov 2011

FreeSwitch ESL for Node.js
[Updated 2012-02-29]

This article uses the old API (pre-0.2) for the esl module; please refer to the documentation for updated examples.

You may also want to look at these two example: a script to modify a channel variable based on a web query and a CouchDB-backed voicemail application.

[Updated 2011-12-05]

esl is a Node.js module that provides both (fully asynchronous) client and server implementations. [Source] [On Github]

The Documentation is now part of the (independent) package.

The module is available on the npm registry. To install:

npm install esl
(The module is written in CoffeeScript but can be used from Javascript/Node.js just like any other module.)

Here is an example (first in CoffeeScript, then in Javascript) for a client which opens a connection, sends one arbitrary API command, and disconnects.

###
(c) 2010 Stephane Alnet
Released under the AGPL3 license
###

esl = require 'esl'

fs_command = (cmd,cb) ->
  client = esl.createClient()
  client.on 'esl_auth_request', (req,res) ->
    res.auth 'ClueCon', (req,res) ->
      res.api cmd, (req,res) ->
        res.exit ->
          client.end()
  if cb?
    client.on 'close', cb
  client.connect(8021, '127.0.0.1')

# Equivalent of the command line  "fs_cli -x reloadxml"
fs_command "reloadxml"

The same code in Javascript (thanks to coffee -c).

var esl, fs_command;
esl = require('esl');
fs_command = function(cmd, cb) {
  var client;
  client = esl.createClient();
  client.on('esl_auth_request', function(req, res) {
    return res.auth('ClueCon', function(req, res) {
      return res.api(cmd, function(req, res) {
        return res.exit(function() {
          return client.end();
        });
      });
    });
  });
  if (cb != null) {
    client.on('close', cb);
  }
  return client.connect(8021, '127.0.0.1');
};
fs_command("reloadxml");

Here is an example (in CoffeeScript) for a prepaid server. This uses a CouchDB database to retrieve and store data, and has some extraneous instrumentation to support the underlying project, but should give you a good idea of how to build a moderately complex application. [Source of the complete application]

###
(c) 2010 Stephane Alnet
Released under the AGPL3 license
###
# This script expects the following variables:
#   ccnq_account       -- account to be decremented
#   target             -- where to bridge the call
#   prepaid_uri        -- URI for the prepaid API

esl = require 'esl'
util = require 'util'
querystring = require 'querystring'
cdb = require 'cdb'

require('ccnq3_config').get (config)->

  # esl.debug = true

  Unique_ID = 'Unique-ID'

  server = esl.createServer (res) ->

    res.connect (req,res) ->

      # Retrieve channel parameters
      channel_data = req.body

      unique_id             = channel_data[Unique_ID]
      util.log "Incoming call UUID = #{unique_id}"

      prepaid_account      = channel_data.variable_ccnq_account
      prepaid_destination  = channel_data.variable_target

      prepaid_cdb = cdb.new config.prepaid.uri

      # Common values
      interval_id = null

      on_disconnect = (req,res) ->
        util.log "Receiving disconnection"
        switch req.headers['Content-Disposition']
          when 'linger'      then res.exit()
          when 'disconnect'  then res.end()

      res.on 'esl_disconnect_notice', on_disconnect


      force_disconnect = (res) ->
        util.log 'Hangup call'
        res.bgapi "uuid_kill #{unique_id}"

      prepaid_cdb.exists (it_does) ->
        if not it_does
          util.log "Database #{channel_data.prepaid_uri} is not accessible."
          return force_disconnect(res)

        # Get account parameters
        prepaid_cdb.get prepaid_account, (r) ->
          if r.error
            util.log "Account #{prepaid_account} Error: #{r.error}"
            return force_disconnect(res)

          interval_duration = r.interval_duration # seconds
          util.log "Account #{prepaid_account} interval duration is #{interval_duration} seconds."

          check_time = (cb) ->
            util.log "Checking account #{prepaid_account}."
            account_key = "\"#{prepaid_account}\""
            options =
              uri: "/_design/prepaid/_view/current?reduce=true&group=true&key=#{querystring.escape(account_key)}"
            prepaid_cdb.req options, (r) ->
              if r.error
                util.log "Error: #{r.error}"
                return force_disconnect(res)

              intervals_remaining = r?.rows?[0]?.value

              if intervals_remaining? and intervals_remaining > 1
                util.log "Account #{prepaid_account} has #{intervals_remaining} intervals left."
                return cb?()

              util.log "Account #{prepaid_account} is exhausted."
              return force_disconnect(res)

          # During call progress, check every ten seconds whether
          # the account is exhausted.
          interval_id = setInterval check_time, 10*1000

          record_interval = (intervals,cb) ->
            util.log "Recording #{intervals} intervals for account #{prepaid_account}."
            rec =
              type: 'interval_record'
              account: prepaid_account
              intervals: - intervals

            prepaid_cdb.post rec, (r) ->
              if r.error
                util.log "Error: #{r.error}"
                return force_disconnect(res)

              util.log "Recorded #{intervals} intervals for account #{prepaid_account}."
              cb?()

          each_interval = (cb) ->
            record_interval 1, () ->
              check_time(cb)

          # Handle ANSWER event
          on_answer = (req,res) ->
            util.log "Call was answered"

            # Clear the ringback timer
            clearInterval interval_id
            interval_id = null

            # First interval for the connected call
            each_interval()

            # Set the in-call timer
            interval_id = setInterval each_interval, interval_duration*1000

            util.log "Call answer processed."

          res.on 'esl_event', (req,res) ->
            switch req.body['Event-Name']

              when 'CHANNEL_ANSWER'
                on_answer(req,res)

              when 'CHANNEL_HANGUP_COMPLETE'
                util.log 'Channel hangup complete'
                clearInterval interval_id
                interval_id = null

              else
                util.log "Unhandled event #{req.body['Event-Name']}"

          on_connect = (req,res) ->

              # Check whether the call can proceed
              check_time () ->

                util.log 'Bridging call'
                res.execute 'bridge', prepaid_destination, (req,res) ->
                  util.log "Call bridged"

          # Handle the incoming connection
          res.linger (req,res) ->
            res.filter Unique_ID, unique_id, (req,res) ->
              res.event_json 'ALL', on_connect

  server.listen(config.prepaid.port)
posted: 2011-11-10 23:09

permanent link

Mon, 07 Nov 2011

ProcessingJS and CoffeeScript

I wanted the background for shimaore.net to be more lively, so I used ProcessingJS and came up with the following CoffeeScript code. (For a demo visit the site.)

###
(c) 2011 Stephane Alnet
###
$(document).ready ->

  width = 327
  height = 421
  actual_width = 3*width

  initial_hue = 160
  hue_increments = 20
  max_hue = 360
  saturation = 90
  brightness = 100
  radius_to_radius = 1.25
  angle_1 = -1
  angle_2 = +1

  sketch = (p) ->

    n_bubbles = 31

    bubbles = []
    hue = initial_hue

    new_bubble = ->
      if bubbles.length > n_bubbles
        bubbles.pop()
      bubbles.unshift
        color: p.color hue, saturation, brightness
        angle: if Math.random() < 0.5 then angle_1 else angle_2

      hue += hue_increments
      hue -= max_hue while hue >= max_hue

    white = p.color 0, 0, 100, 1.0

    p.setup = ->
      p.size actual_width, height
      p.colorMode(p.HSB,max_hue,100,100,1.0)
      p.ellipseMode p.RADIUS
      p.noStroke()

    initial_radius = 0.1
      
    p.draw = ->
      p.background white
      
      x = width / 2.0
      radius = initial_radius
      y = height - radius
      for bubble in bubbles
        if bubble
          p.fill bubble.color
          bubble_radius = radius
          p.ellipse x, y, bubble_radius, bubble_radius

          radius *= radius_to_radius
          x += bubble.angle * (bubble_radius+radius)
          y -= radius

      initial_radius += 0.01
      if initial_radius >= radius_to_radius*0.1
        new_bubble()
        initial_radius = 0.1
      return

  $('#bg').each ->
    p = new Processing(@,sketch)
posted: 2011-11-07 21:27

permanent link

Tue, 18 Oct 2011

Zappa whiteboard app
Today I started a whiteboard app using Zappa. It's usable but mostly a showground for Zappa features (Sammy integration, socket.io) at this time. ;)

Origin is here: git://stephane.shimaore.net/git/zappa-canvas.git [Browse]

Copy on github: git://github.com/shimaore/zappa-canvas.git

posted: 2011-10-18 16:14 tags: zappa

permanent link

Sun, 29 May 2011

Compteur
(This is an initial write-up for this project. More description work is still needed.)

Context

I am one of the "fontainier" at l'ASAEP, our local water association. We own and operate our own water sources, pumping stations, distribution network, up to the water meter at the final user's home.

Our network comprises more than 30 miles of underground pipes, and we needed a tool to better understand the demand on the network. We already have water meter equipped with a small magnet which closes a small switch and therefor can be used to generate pulses. This system is specifically used to control the pumps that inject chlorine in the water.

The goal of the project is therefor to collect data about the amount of water that went through a location, for example at ten minutes intervals. The system must have very low power consumption so that it can work unattended for weeks on regular batteries. (The system might be left for example in a meter box in the middle of a field.)

Environnement du projet

Je suis volontaire à l'ASAEP, notre association locale de distribution de l'eau. Nous possédons et opérons nos propres forages et captages, notre réseau de distribution, nous faisons les relevés de compteurs chez les usagers...

Notre réseau a plus de 50 kms de tuyaux enterrés, et nous avions besoin d'avoir un outil qui nous permette de mieux connaître la demande sur le réseau. Nous disposons déjà de compteurs d'eau qui, équipé d'un petit aimant sur une des roues de comptage, et d'un système de détection, permet de délivrer une impulsion (par fermeture d'un contact). Essentiellement, ce compteur amélioré génère une impulsion par litre. Ce système est utilisé en particulier afin d'asservir les pompes qui injecte le chlore dans l'eau.

Le but du projet est alors de collecter les volumes consommés, par exemple en faisant un relevé toutes les dix minutes. La consommation électrique doit être extrèmement basse, afin de faire fonctionner le circuit pendant des semaines sans intervention sur de simples piles. (L'idée de base étant qu'un tel système peut être installé au milieu d'un champ, avec de faibles opportunités de relevé dans certains cas.)

Technical details

One pulse is received per liter, we use 16 bits counters so up to 65 cubic meters can be measured in one counter.

The amount of data collected is too large to fit comfortably in the RAM or the EEPROM of an AVRtiny, and larger AVRs have too large a power consumption. A DataFlash chip was added to provide data storage. The selected DataFlash offers 264 octets per page (up to 128 measure points, so about 21.5 hours of measuring with a sampling interval of 10 minutes), and 1023 data pages. The first page is used for communication between the USB module and the water-metering module.

Components

The project is split in two parts: Additionally both modules can be programmed via ISP using the pinout of the STK500 ISP6PIN interface. I chose the AVRtiny2313A because it is the component used by the usbtiny library, and it offers low-power usage while having a good deal of interfaces. The same AVR is used on both modules. The Atmel documentation for the DataFlash uses an old layout (especially a RDY/BSY pin that is no longer present on current DataFlash chips); I hope this design will be helpful to people trying to interface and AVR with the current generation of DataFlash. The first module also hosts the memory chip; the interface between the two modules is designed so that the water-meter module releases the DataFlash bus when the USB module is connected.

Water-meter module

This module has a LED used for debugging purposes, a two AA-battery power supply, a connector for the water-meter magnet-activated switch, a connector for the USB module and ISP, the Atmel microcontroller and flash.

The microcontroller is programmed by connecting the STK500 ISP6PIN port to J1 on pin 1-6. The connection with the USB module is done using pin 1-6 and 9-10 (pins 7-8 are left unused to allow for cable orientation).

At startup the LED will flash a couple seconds to indicate startup and detection of the DataFlash module, then go steady off if everything is working. The LED will flash on/off every four seconds if the system is ready but no pulse is received from the metering switch.

The module doesn't have a crystal and uses the (low-power) internal 128kHz clock of the AVR. However the clock is not extremely precise (it runs at roughly 110kHz around 3V) and this has to be compensated for.

The water-meter AVR is taken off the DataFlash bus (DO, DI, SCK, Flash CS) by activating the Flash RESET pin. Once the Flash RESET pin is brought back up, the Flash is available to the USB module. At the end of its use of the Flash, the USB module brings the Flash RESET pin back down (active) to indicates the Flash bus can be taken back by the water-meter AVR.

USB module

This module has a LED for debugging purposes (controlled by the PC connected to the USB bus), a 12MHz crystal, a USB connector, and the same J1 connector as the water-meter module. The power supply is local (two AA-batteries).

The microcontroller is programmed through pin 1-6, while the entire connector (pins 1-6 and 9-10) must be connected to the water-module for access to the DataFlash.

The USB bus uses 3V voltages, so no adaptation is needed as long as the battery power supply is used.

All operations are controlled by the host connected on the USB bus: Flash Reset, sending commands and receiving data, etc. Due to limitations in the usbtiny design the data is transferred by blocks of 8 octets at one time.

This module can probably be used as a generic USB-to-DataFlash transfer module.

USB client (on PC host)

The USB software on the PC host is designed for Linux with libusb 1.0.

The software will issue the commands through the USB module in order to take the water-meter module's AVR offline, retrieve data from the DataFlash module, and save new data (especially realtime clock) onto the flash.

Source code

git clone git://shimaore.net/git/compteur.git

View Online

posted: 2011-05-29 17:38

permanent link

Wed, 02 Feb 2011

ANSam vs CED

Thought I should share these.

First a waveform view of two call start tones sent by two different fax machines. The top one is a SuperG3 fax, it sends an ANSam tone (specified in ITU V.8); the first giveaway is the 15Hz amplitude modulation which shows up as little bumps and valleys on the waveform. The bottom fax is a regular G3 fax machine, it sends a CED tone (specified in ITU T.30); the main signal is also at 2100Hz but there is no modulation.

Second a spectrum view of the two calls. The traces are VoIP traces so they aren't completely clean, but looking at the bottom trace it is clear that there aren't regular interruptions in the spectrum of the first tone sent out. On the contrary the top row shows regular bumps; these are actually phase reversals every 450ms (again, per V.8) which indicate to potential echo cancellers along the way that they should take themselves out of the path for the duration of this call.

Analysis done using Wireshark's RTP Payload export, and Audacity.

posted: 2011-02-02 22:54 tags: fax, modem, analysis

permanent link

Thu, 23 Dec 2010

Node.js AMQP for version 0.9.1

Ryan Dahl's node-amqp is great but I needed support for the new headers type of exchange, so I updated it to support AMQP 0.9.1.

Update: node-amqp trunk (https://github.com/postwait/node-amqp) merged my changes, no need to use node-amqp-091 anymore. I assume the examples below still work though I haven't tested them.

Here are a couple examples showing how to use the new bind_headers method I introduced. First a client that subscribes to a queue which uses the headers:

#!/usr/bin/env node
# Use node-amqp for AMQP 0.9.1.
var amqp = require('node-amqp-091');
var util = require('util');

var connection = amqp.createConnection({ host: 'localhost' });

# Wait for connection to become established.
connection.addListener('ready', function () {
  util.debug('Connected');
  # Create a queue and bind to all messages.
  var q = connection.queue('my-queue');
  # Catch all messages
  # Use the default 'amq.headers' exchange
  # Note: this example only filters on one value, add yours.
  # x-match can be either 'any' or 'all'.
  q.bind_headers({"x-match":'any',where:'here'});

  # Receive messages
  q.subscribe(function (message) {
    # Print messages to stdout
    util.debug(util.inspect(message));
  });
});

Then here is an example for a provider that sends messages to that queue:

#!/usr/bin/env node

var amqp = require('node-amqp-091');
var util = require('util');

var connection = amqp.createConnection({ host: 'localhost' });

# Wait for connection to become established.
connection.addListener('ready', function () {
  util.debug('Connected');
  # Connect to the default 'headers' exchange
  var exch = connection.exchange('amq.headers');
  exch.addListener('open', function () {
    util.debug('Open');
    while(true) {
      body = {name:'bob',difficulty:'alive'};
      options = {headers:{name:'bob',where:'here',when:'now'}};
      # The key will not be used in a headers exchange.
      exch.publish('test.bob.is.alive',body,options);
    }
  });
});

posted: 2010-12-23 16:35

permanent link

Thu, 16 Dec 2010

CouchDB IPv6

CouchDB's configuration's address parameter under [httpd] maps directly to a Mochiweb parameter. It seems it only supports one IP address. So:

[httpd]
address = ::
will bind to any IPv6 address on the system.

To keep IPv4 and IPv6 connectivity, under Linux, you might need to make sure that /proc/sys/net/ipv6/bindv6only (net.ipv6.bindv6only in /etc/sysctl.conf) is 0 (see the discussion related to Debian bug 560238).

On other OSes (where V6ONLY is the default) it seems that CouchDB will be single stack.

posted: 2010-12-16 14:42

permanent link

Wed, 30 Jun 2010

Routage des appels d'urgence
Version mise à jour d'un document que j'avais préparé dans le cadre d'améliorer le routage des appels d'urgence en France, proposant le concept d'une association dédiée. posted: 2010-06-30 23:47 tags: emergency

permanent link

Mon, 07 Jun 2010

Title: CouchDB shared calendar

I've posted my version of jchrisa's Calendar CouchApp. It uses the new couchapp framework (evently, CouchDB sessions), CouchDB 0.11.0, and supports timezones, repeat events.

To get it:

 git clone git://shimaore.net/git/cal2.git 

You'll need to download and install CouchApp to use it.

posted: 2010-06-07 14:14 tags: couchdb, couchapp, calendar

permanent link

Fri, 28 May 2010

Title: ejabberd configuration for dual-stack (IPv4, IPv6) host

%% Accept connections on the same port on IPv4 and IPv6:
{listen,
 [
  {{5222, "::"}, ejabberd_c2s, [ inet6,
                        {access, c2s},
                        {shaper, c2s_shaper},
                        {max_stanza_size, 524288},
                        starttls, {certfile, "/etc/ejabberd/ejabberd.pem"}
                       ]},
  {{5222, "0.0.0.0"}, ejabberd_c2s, [
                        {access, c2s},
                        {shaper, c2s_shaper},
                        {max_stanza_size, 524288},
                        starttls, {certfile, "/etc/ejabberd/ejabberd.pem"}
                       ]},
  {{5269, "::"}, ejabberd_s2s_in, [ inet6,
                           {shaper, s2s_shaper},
                           {max_stanza_size, 524288}
                          ]},
  {{5269, "0.0.0.0"}, ejabberd_s2s_in, [
                           {shaper, s2s_shaper},
                           {max_stanza_size, 524288}
                          ]},
  %% etc.
]}.

%% Try IPv6 first when doing s2s attempts:
{outgoing_s2s_options, [ipv6, ipv4], 10000}.

posted: 2010-05-28 12:52 tags: ejabberd

permanent link

Sat, 20 Feb 2010

French for FreeSwitch

After Neuronnexion paid for Philippe Leroy and Sibylle Luperce to record french audio prompts for FreeSwitch, I updated mod_say_fr to match them.

There are more audio files to come, but I need to come up with proper filenames for them. I'll add them to the Archive.org repository at http://www.archive.org/details/FrenchAudioFilesForFreeswitch once this is ready.

We recorded non-France prompts in the archive, like septante, octante, nonante. I don't know the different locales (Belgium, Canada, ...) well enough to be able to do anything useful with them; but if someone does I'll be happy to update mod_say_fr to match. We could for example use a LOCALE channel variable to track the actual locale; this would also allow to use the proper monetary units by default.

posted: 2010-02-20 22:14 tags: freeswitch, french

permanent link

Tue, 20 Oct 2009

ejabberd clustering
I used the excellent notes at the reference article. Here are additional notes for Debian:

Also the primary server is the one where the web UI should be used; the other one(s) replicate the database.

Here's my complete /etc/default/ejabberd on jabber1:

SMP=auto
ERLANG_NODE=ejabberd

Here's my complete /etc/default/ejabberd on jabber2:

SMP=auto
ERL_OPTIONS=" -mnesia extra_db_nodes ['ejabberd@jabber1'] -s mnesia "
ERLANG_NODE=ejabberd

(If I get time I might rewrite this article into complete instructions using the reference article as a basis.)

posted: 2009-10-20 11:40

permanent link

Tue, 29 Sep 2009

Bad Robot

Building on the example script in AnyEvent::XMPP I built a small robot which can send messages to either an individual user or a jabber chat room, from either stdin or a fifo. We use this at work to get alerts into a chat room we use for support activities.

Update: a more complex solution is used as the main driver for xmpp-agent.pl in CCNQ2.0

posted: 2009-09-29 03:17

permanent link

Mon, 28 Sep 2009

Huawei E160E (Orange 3G+) on Debian Lenny
As seen on the Internet:

# /etc/chatscripts/orange
ABORT BUSY
ABORT "NO CARRIER"
ABORT VOICE
ABORT "NO DIALTONE"
ABORT ERROR
'' ATZ
# Check SIM pin should return ready
OK-AT-OK 'AT+CPIN?'
## OK-AT-OK 'AT+CPIN="...."'
OK-AT-OK 'AT+CGDCONT=?'
OK-AT-OK 'AT+CGATT=?'
OK-AT-OK 'AT+CGREG=?'
# Verify we are connected to a network (will show the network name)
OK-AT-OK 'AT+COPS?'
OK-AT-OK 'AT+CGDCONT=3,"IP","orange.fr"'
OK-AT-OK 'AT&FE0Q0V1'
OK-AT-OK 'ATD*99***3#'
CONNECT ""
and
# /etc/ppp/peers/orange
noauth
connect "/usr/sbin/chat -v -f /etc/chatscripts/orange"
debug
/dev/ttyUSB0
user "orange"
password "orange"
defaultroute
noipdefault
noccp
novj
then:
pon orange
(I'm not 100% sure "noccp" and "novj" are needed, but it works.)

Update: I've since installed Ubuntu "Netbook" on my Asus EeePC; the Huawei key is recognized and proper scripts provided for Orange.fr out of the box.

posted: 2009-09-28 06:15

permanent link

Fri, 25 Sep 2009

Changing timezone in Debian

dpkg-reconfigure tzdata
posted: 2009-09-25 03:18

permanent link

Mon, 07 Sep 2009

Xen and VLANs

Nothing you can't find elsewhere on the Internet in this post, just taking some notes.

This is for Debian Lenny (Xen 3.3). The idea is to connect eth0 to a switch that supports VLANs. The native VLAN is used, along with other VLANs.

Update: You must have an IP address specified for your eth0 interface. I lost a couple servers during their installation phase that way.

posted: 2009-09-07 06:05

permanent link

Tue, 07 Jul 2009

Présentation RMLL 2009 Nantes
Documents au format OpenDocument:

posted: 2009-07-07 06:14

permanent link

Tue, 19 May 2009

Backporting sip_cid_type to FreeSwitch 1.0.3

A simple patch to back-port the sip_cid_type patch from trunk to 1.0.3.

posted: 2009-05-19 06:08

permanent link

Mon, 11 May 2009

Useful reduce functions for CouchDb

Essentially taking some notes for myself..

posted: 2009-05-11 07:18

permanent link

Fri, 03 Apr 2009

Yxa CNAM code posted

I finally cleaned up my Yxa/erlang code for Verisign CNAM and released it. In the configuration we're using in production, the server (running Yaws embedded inside Yxa) is essentially a web-to-SIP gateway for CNAM query; the web queries are generated by Perl clients inside FreeSwitch, which take care of updating the SIP From fields for the INVITEs.

You can find my code on github -- this is Yxa SVN release 1686, with my CNAM additions/changes.

I also provide a template which should go in ~yxa/src/event/ybed.erl and needs to be adjusted to your local settings. This file runs Yaws embedded inside Yxa in order to trigger the SIP events.

As seen on the Yxa-Devel list

posted: 2009-04-03 21:10 tags: CNAM, erlang, yaws, Yxa

permanent link

Sat, 21 Mar 2009

Better FollowMe for FreeSwitch

The problem

The examples for Follow-Me on FreeSwitch had always left me wanted; two issues specifically were pretty frustrating:
  1. Most examples use the common method of doing a bridge command with failover (using the call_timeout channel variable), for example:
     <action application="bridge"
      data="${extension}|${extension},${followme}"/>
    FreeSwitch will do what it is being told: generate a call setup to the extension, then take that call back, then place a new (failover) call to both the extension and the followme. This causes issues if the user picks up the call right before the failover, since they may pick up the phone, only to answer a call that was just taken back, and see a new call from the same caller come up right afterwards. More importantly, if the user only has a single line appearance, then the call will not ring back (since the extension is busy) and will be lost.
  2. I was used to FreePBX's call confirmation feature, and although I could recreate it with a group_confirm script, since the scripts cannot be assigned to a specific outbound leg on a call, my script ended up being non-generic (doing a regex match on the destination number to figure out whether call confirmation was needed or not).

The solution

Until yesterday, that is. I read the bridge documentation carefully, and re-read the channel variables documentation carefully, and figured out:
  1. Using the leg_delay_start parameter, there is no need to rely on failover. It's sufficient to delay the follow-me call setup:
     <action application="bridge"
      data="${extension},[leg_delay_start=12]${followme}"/> 
    In that last example, the extension will start ringing immediately, and the followme starts ringing after 12 seconds; the call to the original extension is never taken back, so the glare scenario I described earlier is eliminated.
  2. Once I had figured out per-leg parameters, I tried to see whether I could create my own parameter. It worked... To do per-destination call confirmation, you can write for example:
     <action application"bridge"
      data="${extension},[leg_delay_start=12,leg_confirm=y]${followme}"/> 
    In this example the original extension will not get call confirmation, but the followme destination will.
Putting these two elements together, a FreeSwitch XML script for follow-me would look like this:
 <action application="set"
  data="group_confirm_key=exec"/>
 <action application="set"
  data="group_confirm_file=javascript confirm.js"/>
 <action application="bridge"
  data="${extension},[leg_delay_start=12,leg_confirm=y]${followme}"/>
Here is confirm.js:
//  confirm.js - FreeSwitch call confirmation script
//  (c) 2009 - Stéphane Alnet
//  License: GPL2 or above

console_log("info", "Destination: "+ session.destination + "\n");
if(!session.getVariable('leg_confirm'))
{
  console_log("info", "No need to confirm, connect the call!\n");
  exit();
}

var confirmed = false;
var confirmation_digit = "1";
var try_count = 6;
var prompt_file = "connect-to-caller-press-1.wav";

function onInput( session, type, data, arg ) {
  if ( type == "dtmf" ) {
    console_log( "info", "Got digit "..data.digit.."\n" );
    if ( data.digit == confirmation_digit ) {
      confirmed = true;
      console_log( "info", "Confirming session..\n" );
      return(false);
    }
  }
  return(true);
}

if ( session.ready() ) 
{
  session.answer();
  session.flushDigits();
  console_log("info", "Starting confirmation\n");
  var count = try_count;
  while( session.ready() && ! confirmed && count-- > 0 )
  {
    session.execute("sleep","200");
    session.streamFile( prompt_file, onInput );
  }
  if( ! confirmed )
  {
    console_log("info", "Not confirmed\n");
    session.hangup();
  }
  else
  {
    console_log("info", "Confirmed\n");
  }
}
else
{
  console_log("info", "Session is not ready.\n");
}

Note: the value of the leg_confirm variable is never used. If the variable is present, call confirmation will happen. The script could be modified to use the value of the variable as the confirmation digit, instead of using a hard-coded value of "1". This would also require to use a dynamic prompt, instead of the static connect-to-caller-press-1.wav filename embedded in the script.

posted: 2009-03-21 07:08

permanent link

Wed, 21 Jan 2009

Yxa

We signed up for Verisign CNAM, so I'm starting to work on the CNAM Query piece of the equation (the CNAM Storage side is relatively straightforward). Specifically the fun Verisign specs (apparently claimed by Verizon under patent application 20080240383).

Looking into the source for Yxa it seems I should be able to get things together relatively easily, assuming I can get something similar to active_subscriber in place, and write a callingname_package to handle the specs. At this time I think I'll interface FreeSwitch via (Javascript? Lua?) to a REST/Yaws web server that will query first an ets table (for example if we want to pre-store some data, like our own customers) and if nothing is present, forward to Yxa. The details will emerge over the next few days.

This should be fun. :} (I've actually wanted to write this code for a very long time, and the time has finally cometh!)

Update: The code is now in production. I left the ets idea out for now, and implemented the FreeSwitch module in Perl because I couldn't package Javascript-Curl on Debian; but the outline above is what I implemented. I'll post the code on the yxa list once I'll have a chance to clean it up.

Update(2): The code has been posted.

posted: 2009-01-21 23:47 tags: erlang, Yxa

permanent link

Fri, 16 Jan 2009

CouchDb as FreeSwitch provisioning database
I had the idea of trying to use CouchDb as provisioning database for FreeSwitch, and started with the easiest component, the dialplan. Here's a short overview of how I did.

FreeSwitch

I enabled mod_xml_curl module (in modules.conf.xml) and configured the URL for yaws in xml_curl.conf.xml:

<param name="gateway-url" value="https://couchdb-proxy.example.com:8443/dialplan.yaws" bindings="dialplan"/>

A restart of FreeSwitch is required (or use "load mod_xml_curl" at the CLI).

Installing ecouch in Yaws

The key to the process is a proxy between the POST format used by xml_curl and the REST format used by CouchDb. I decided to write this proxy using erlang; it doesn't have to. In my case the proxy is a yaws modapp; since I was going to be using ecouch to access CouchDb from within my erlang code, I first added the following line in yaws.conf in order to activate mod_ecouch, a small erlang module I created which starts inets and ecouch:

runmod = mod_ecouch

mod_ecouch itself is very simple:

-module(mod_ecouch).
-author('stephane@shimaore.net').

-export([start/0,stop/0]).

start() ->
  application:start(inets),
  application:start(ecouch).

stop() ->
  application:stop(ecouch),
  application:stop(inets).

Of course ecouch has to be installed (and compiled) in the default erlang library path first; on my Debian system that was /usr/lib/erlang/lib. Note: I used rfc4627.erl from Lshift to replace the incomplete version found in the original mod_ecouch.

Erlang ecouch Proxy for FreeSwitch

Then in the yaws root directory I created the following yaws script which will take a parameterized query from FreeSwitch's mod_xml_curl and transform it into a query for CouchDb. This script is the dialplan.yaws script I referenced in the configuration for mod_xml_curl earlier. To keep things very generic, the script only looks at the Hunt-Context and Hunt-Destination-Number fields provided by FreeSwitch and use them to gather a XML extension from CouchDb. If more specific routing needs to be done (for example, Caller-ID-based routing), it can be done inside the XML code generated by CouchDb (more on this later).

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="freeswitch/xml">
  <section name="dialplan" description="dialplan reply">
<erl>

out(A) ->

  Timeout = 500,

  %% We accept GET and POST (FS uses POST)
  case Q1 = queryvar(A,"Hunt-Context") of
    {ok, Context} ->
      {ok, Number } = queryvar(A,"Hunt-Destination-Number");
    _ ->
      {ok, Context} = postvar(A,"Hunt-Context"),
      {ok, Number } = postvar(A,"Hunt-Destination-Number")
  end,

  %% Query CouchDb
  Key = yaws_api:url_encode("\"" ++ Number ++ "@" ++ Context ++ "\""),

  Requestor = self(),

  spawn(fun() -> 
    Requestor ! ecouch:doc_get("dialplan", "_view/freeswitch/xml?key=" ++ Key)
  end),

  receive
    {ok, Json } ->
      %% Find the rows in the JSON reply
      { ok, Rows } = rfc4627:get_field(Json,"rows"),
      %% Concatenate the JSON "value" field in all the rows.
      Result = lists:flatten(lists:map(fun(Row) -> { ok, Entry } = rfc4627:get_field(Row,"value"), binary_to_list(Entry) end,Rows)),
      case Result of
        [] ->
          [{status,404}]; %% No results found, ask FS to fallback to file-based XML
        _ -> 
          {content,"text/xml",Result}
      end;
    _ ->
      [{status,503}]  %% Service Unavailable
  after Timeout ->
      [{status,504}]  %% Gateway Timeout
  end.

</erl>
  </section>
</document>

CouchDb records

In CouchDb I create records with context, extension (for key matching), and local_context, local_extension for the destinaton (for example -- the main idea is that the script in CouchDb is where the XML formatting takes place, so that the database is self-contained and the proxy code (above) can be kept very generic).

CouchDb View

In CouchDb I created a view called "xml" in the "freeswitch" design document which looks like this:

function(doc) {

  var xml_header =
    "<context name=\""+doc.context+"\">" +
    "<extension name=\""+doc.extension+"\">" +
    "<condition field=\"destination_number\" expression=\"^("+doc.extension+")$\">";

  var xml_footer =
    "</condition></extension></context>";

  var key = doc.extension+'@'+doc.context;

  if(doc.local_extension && doc.local_context )
  {
    var xml =
      "<action application=\"transfer\" data=\""+doc.local_extension+" XML "+doc.local_context+"\"/>";
    emit(key,xml_header+xml+xml_footer);
    return;
  }
}

This view takes regular records and map them to XML snippets that the yaws script will then feed back to FreeSwitch. As I mentioned, the script is where the flexibility of CouchDb can be harnessed to create different profiles based on different fields found in the database during the map process. This is also where the generic (destination-only) record selection process done by the erlang proxy code in dialplan.yaws can be refined.

Update: This has been in production for a while and works as advertised. posted: 2009-01-16 22:26 tags: CouchDb, erlang, FreeSwitch, mod_xml_curl, yaws

permanent link

Sun, 11 Jan 2009

Firefly/mt-daapd network tweaks
Playing with Firefly/mt-daapd tonight, a couple notes:

posted: 2009-01-12 00:58

permanent link

Recipe for FreeSwitch and RFC2833

I'm using FreeSwitch both to terminate calls (e.g. into and IVR or Voicemail) and switch calls (e.g. between a SIP trunk and a SIP phone or ATA). I try to select endpoints and carriers that do DTMF properly (which I know because things work through OpenSER), so I prefer when FreeSwitch do not alter the RFC2833 events. I found the following recipe to work pretty well:

Make DTMF pass-thru by default

In the sofia sip_profile, I use <param name="dtmf-type" value="rfc2833"/> <param name="rfc2833-pt" value="101"/> <param name="pass-rfc2833" value="true"/> This makes DTMF as RFC2833, and by default FreeSwitch will pass the RFC2833 events without modifying them.

Remove pass-thru when FreeSwitch must handle DTMF

However this prevents FreeSwitch from interpreting RFC2833 events when it terminates the call itselfs (in an IVR or voicemail, for example). Thus I modified my dialplan scripts to remove the pass-thru of RFC2833 events before FreeSwitch answers the call: <action application="set" data="pass_rfc2833=false"/> <action application="answer"/> This works as expected. I suspect that if I had to do a bridge after the call is answered by FreeSwitch I would have to add {pass-rfc2833=true} to it, but haven't tested this.

Update: My configuration has changed somewhat since. Among other things I'm using auto-rtp-bugs clear when I'm relaying media (e.g. when handling NAT). tags: DTMF, FreeSwitch posted: 2009-01-11 21:26

permanent link

Wed, 11 Jul 2007

At RMLL
This week I'm spending some time in France at the Rencontres Mondiales du Logiciel Libre. The conference is a success, with a lot of attendees, and presenters from all over the world. I'll be presenting on Friday on OpenSource Telephony and after my presentation animating a workshop. posted: 2007-07-11 04:36

permanent link