Read samples from two channels and write to file

Hello,
I am a student at the University of Aveiro, Portugal and I have just
started working at a project with GNU Radio and the USRP board.
I have a question regarding the sample acquisition.
I need to read the samples from both channels of a Basic Rx and then to
write them in a file.
Until now I have used the usrp_rx_cfile.py file, but it allows the
acquisition from only one channel. My question is: can I acquire data in
the same, from both channels?
Thanks for any help.
Diana

On Wed, Apr 23, 2008 at 8:38 AM, Diana I. [email protected]
wrote:

Diana
I believe this might be answered in the FAQ for you:

http://gnuradio.org/trac/wiki/FAQ#HowcanIrecorddatafromdifferentdaughterboardsonthesameUSRP

Take a look at the code linked there?

Brian

Brian P. wrote:

Thanks for any help.
Diana

I believe this might be answered in the FAQ for you:

http://gnuradio.org/trac/wiki/FAQ#HowcanIrecorddatafromdifferentdaughterboardsonthesameUSRP

Take a look at the code linked there?

I think their question is slightly different, though we’ve gotten this
one on the list numerous times so we should toss it in the FAQ. It’s
how to enable both input channels on a single daughterboard, I think
that’s the question? For example, with BasicRX which has two input
ports.

  • George

On Wed, Apr 23, 2008 at 5:38 AM, Diana I. [email protected]
wrote:

Diana

Posted via http://www.ruby-forum.com/.

I recently finished doing exactly this. The following code works for
me. I modified this from the multi_fft example file.

n_chans = 2;
self.rx_src = usrp.source_c()
rx_subdev = self.rx_src.db[0]+self.rx_src.db[1]
self.rx_src.set_mux(gru.hexint(0xf1f0f3f2))
deinterleaver = gr.deinterleave(gr.sizeof_gr_complex)
self.rx_src_fft0 = fftsink2.fft_sink_c(panel,title=“Ch
RXB-A”,fft_size=fftsize_N,sample_rate=rx_rate/sw_decim)
self.rx_src_fft0 = fftsink2.fft_sink_c(panel,title=“Ch
RXB-B”,fft_size=fftsize_N,sample_rate=rx_rate/sw_decim)

self.connect(self.rx_src,deinterleaver)
self.connect((deinterleaver,0),self.rx_src_fft0)
self.connect((deinterleaver,1),self.rx_src_fft1)
vbox.Add(self.rx_src_fft0.win,10,wx.EXPAND)
vbox.Add(self.rx_src_fft1.win,10,wx.EXPAND)

#Tune the receiver
for i in range(len(rx_subdev)):
r = self.rx_src.tune(rx_subdev[i]._which,
rx_subdev[i],carrier_freq)
if not r:
print “set_freq: failed to set subdev[%d] freq to %f” %
(i, carrier_freq)

RXB-A appears on (deinterleaver,0) ad RXB-B appears on
(deinterleaver,1). If you are using RXA then you might want to replace
the mux value by something like 0xf3f2f1f0.

Regards,
Karthik


www.stanford.edu/~karthikv

I tryed your code, but it’s not working. I get a lot of errors.
This is the complete program i have written(I’ve modified the
multi-fft.py file with the lines you wrote me).
Can you explain what this line does:
rx_subdev = self.rx_src.db[0]+self.rx_src.db[1]? The numbers 0 and 1
refer to inputs from the same Basic RX or to different Basix Rx boards?
I have connected only one Basic Rx.
Thank you very much for your help,
Diana

#!/usr/bin/env python
from gnuradio import gr, gru, eng_notation
from gnuradio import usrp
from gnuradio.eng_option import eng_option
from gnuradio import eng_notation
from gnuradio import optfir
from optparse import OptionParser
from gnuradio.wxgui import stdgui, fftsink2, waterfallsink, scopesink,
form, slider
import wx
from usrpm import usrp_dbid
import time
import os.path
import sys
class my_graph(stdgui.gui_flow_graph):

def __init__(self, frame, panel, vbox, argv):
    stdgui.gui_flow_graph.__init__(self)

    self.frame = frame
    self.panel = panel
usage="%prog: [options]"
    parser = OptionParser (option_class=eng_option,usage=usage)
    #parser.add_option("-S", "--subdev", type="subdev", default=(0, 

None),
# help=“select USRP Rx side A or B
(default=A)”)
parser.add_option("-d", “–decim”, type=“int”, default=128,
help=“set fgpa decimation rate to DECIM
[default=%default]”)
parser.add_option("-f", “–freq”, type=“eng_float”,
default=10.7e6,
help=“set frequency to FREQ
[default=%default])”, metavar=“FREQ”)
parser.add_option("-g", “–gain”, type=“eng_float”, default=0,
help=“set gain in dB [default=%default]”)
parser.add_option("-F", “–filter”, action=“store_true”,
default=True,
help=“Enable channel filter”)
(options, args) = parser.parse_args()

    if len(args) != 1:
        parser.print_help()
        raise SystemExit,1

n_chans = 2
fftsize_N=512
if options.filter:
        sw_decim = 4
    else:
        sw_decim = 1

self.rx_src = usrp.source_c()

if self.rx_src.nddc() < n_chans:
        sys.stderr.write('This code requires an FPGA build with %d 

DDCs. This FPGA has only %d.\n’ % (n_chans, self.rx_src.nddc()))
raise SystemExit,1

if not self.rx_src.set_nchannels(n_chans):
        sys.stderr.write('set_nchannels(%d) failed\n' % (n_chans,))
        raise SystemExit,1

    rx_rate = self.rx_src.adc_freq() / self.rx_src.decim_rate()
    print "USB data rate   = %s" % 

(eng_notation.num_to_str(rx_rate),)
print “Scope data rate = %s” %
(eng_notation.num_to_str(rx_rate/sw_decim),)

rx_subdev = self.rx_src.db[0]+self.rx_src.db[1]

self.rx_src.set_mux(gru.hexint(0xf1f0f3f2))

deinterleaver = gr.deinterleave(gr.sizeof_gr_complex)

for i in range(nchan):
       scope = fftsink.fft_sink_c(self, panel, 

sample_rate=input_rate/sw_decim,
title=“Input %d” % (i,),
ref_level=80, y_per_div=20)
vbox.Add(scope.win, 10, wx.EXPAND)
for i in range(n_chans)
self.rx_src_fft0 =
fftsink2.fft_sink_c(panel,title=“ChRXB-A”,fft_size=fftsize_N,sample_rate=rx_rate/sw_decim)
vbox.Add(self.rx_src_fft0.win,10,wx.EXPAND)
for i in range(n_chans)
self.rx_src_fft1 =
fftsink2.fft_sink_c(panel,title=“ChRXB-B”,fft_size=fftsize_N,sample_rate=rx_rate/sw_decim)
vbox.Add(self.rx_src_fft1.win,10,wx.EXPAND)

self.connect(self.rx_src,deinterleaver)

self.connect((deinterleaver,0),self.rx_src_fft0)
self.connect((deinterleaver,1),self.rx_src_fft1)



chan_filt_coeffs = optfir.low_pass (1,           # gain
                                        rx_rate,  # sampling rate
                                        80e3,        # passband 

cutoff
115e3, # stopband
cutoff
0.1, # passband
ripple
60) # stopband
attenuation
#print len(chan_filt_coeffs)

self.set_gain(options.gain)
    self.set_freq(options.freq)

def set_gain(self, gain):
    for i in range(len(self.subdev)):
        self.rx_subdev[i].set_gain(gain)

#Tune the receiver
def set_freq(self, target_freq):
ok = True
for i in range(len(rx_subdev)):
r = self.rx_src.tune(rx_subdev[i]._which, i, rx_subdev[i],
target_freq)
if not r:
ok = False
print “set_freq: failed to set subdev[%d] freq to %f” %
(i, target_freq)

    return ok

def main ():
app = stdgui.stdapp(my_graph, “Multi Scope”, nstatus=1)
app.MainLoop()

if name == ‘main’:
main ()
Karthik Vijayraghavan wrote:

On Wed, Apr 23, 2008 at 5:38 AM, Diana I. removed_email_address[email protected]
wrote:

Diana

Posted via http://www.ruby-forum.com/.

I recently finished doing exactly this. The following code works for
me. I modified this from the multi_fft example file.

n_chans = 2;
self.rx_src = usrp.source_c()
rx_subdev = self.rx_src.db[0]+self.rx_src.db[1]
self.rx_src.set_mux(gru.hexint(0xf1f0f3f2))
deinterleaver = gr.deinterleave(gr.sizeof_gr_complex)
self.rx_src_fft0 = fftsink2.fft_sink_c(panel,title=“Ch
RXB-A”,fft_size=fftsize_N,sample_rate=rx_rate/sw_decim)
self.rx_src_fft0 = fftsink2.fft_sink_c(panel,title=“Ch
RXB-B”,fft_size=fftsize_N,sample_rate=rx_rate/sw_decim)

self.connect(self.rx_src,deinterleaver)
self.connect((deinterleaver,0),self.rx_src_fft0)
self.connect((deinterleaver,1),self.rx_src_fft1)
vbox.Add(self.rx_src_fft0.win,10,wx.EXPAND)
vbox.Add(self.rx_src_fft1.win,10,wx.EXPAND)

#Tune the receiver
for i in range(len(rx_subdev)):
r = self.rx_src.tune(rx_subdev[i]._which,
rx_subdev[i],carrier_freq)
if not r:
print “set_freq: failed to set subdev[%d] freq to %f” %
(i, carrier_freq)

RXB-A appears on (deinterleaver,0) ad RXB-B appears on
(deinterleaver,1). If you are using RXA then you might want to replace
the mux value by something like 0xf3f2f1f0.

Regards,
Karthik


www.stanford.edu/~karthikv

Diana I. wrote:

I’m sorry, the correct code(which is not working) is the followig:

Tell us what your errors are.

  • George

I’m sorry, the correct code(which is not working) is the followig:

#!/usr/bin/env python
from gnuradio import gr, gru, eng_notation
from gnuradio import usrp
from gnuradio.eng_option import eng_option
from gnuradio import eng_notation
from gnuradio import optfir
from optparse import OptionParser
from gnuradio.wxgui import stdgui, fftsink2, waterfallsink, scopesink,
form, slider
import wx
from usrpm import usrp_dbid
import time
import os.path
import sys
class my_graph(stdgui.gui_flow_graph):

def __init__(self, frame, panel, vbox, argv):
    stdgui.gui_flow_graph.__init__(self)

    self.frame = frame
    self.panel = panel
usage="%prog: [options]"
    parser = OptionParser (option_class=eng_option,usage=usage)
    #parser.add_option("-S", "--subdev", type="subdev", default=(0, 

None),
# help=“select USRP Rx side A or B
(default=A)”)
parser.add_option("-d", “–decim”, type=“int”, default=128,
help=“set fgpa decimation rate to DECIM
[default=%default]”)
parser.add_option("-f", “–freq”, type=“eng_float”,
default=10.7e6,
help=“set frequency to FREQ
[default=%default])”, metavar=“FREQ”)
parser.add_option("-g", “–gain”, type=“eng_float”, default=0,
help=“set gain in dB [default=%default]”)
parser.add_option("-F", “–filter”, action=“store_true”,
default=True,
help=“Enable channel filter”)
(options, args) = parser.parse_args()

    if len(args) != 0:
        parser.print_help()
        raise SystemExit,1

n_chans = 2
fftsize_N=512
if options.filter:
        sw_decim = 4
    else:
        sw_decim = 1

self.rx_src = usrp.source_c()

if self.rx_src.nddc() < n_chans:
        sys.stderr.write('This code requires an FPGA build with %d 

DDCs. This FPGA has only %d.\n’ % (n_chans, self.rx_src.nddc()))
raise SystemExit,1

if not self.rx_src.set_nchannels(n_chans):
        sys.stderr.write('set_nchannels(%d) failed\n' % (n_chans,))
        raise SystemExit,1

    rx_rate = self.rx_src.adc_freq() / self.rx_src.decim_rate()
    print "USB data rate   = %s" % 

(eng_notation.num_to_str(rx_rate),)
print “Scope data rate = %s” %
(eng_notation.num_to_str(rx_rate/sw_decim),)

rx_subdev = self.rx_src.db[0]+self.rx_src.db[1]

self.rx_src.set_mux(gru.hexint(0xf1f0f3f2))

deinterleaver = gr.deinterleave(gr.sizeof_gr_complex)


self.rx_src_fft0 = 

fftsink2.fft_sink_c(panel,title=“ChRXB-A”,fft_size=fftsize_N,sample_rate=rx_rate/sw_decim)
self.connect(self.rx_src,deinterleaver)
vbox.Add(self.rx_src_fft0.win,10,wx.EXPAND)

self.rx_src_fft1 = 

fftsink2.fft_sink_c(panel,title=“ChRXB-B”,fft_size=fftsize_N,sample_rate=rx_rate/sw_decim)
vbox.Add(self.rx_src_fft1.win,10,wx.EXPAND)

self.connect((deinterleaver,0),self.rx_src_fft0)
self.connect((deinterleaver,1),self.rx_src_fft1)



self.set_gain(options.gain)
    self.set_freq(options.freq)

def set_gain(self, gain):
    for i in range(len(self.subdev)):
        self.rx_subdev[i].set_gain(gain)

#Tune the receiver
def set_freq(self, target_freq):
ok = True
for i in range(len(rx_subdev)):
r = self.rx_src.tune(rx_subdev[i]._which, i, rx_subdev[i],
target_freq)
if not r:
ok = False
print “set_freq: failed to set subdev[%d] freq to %f” %
(i, target_freq)

    return ok

def main ():
app = stdgui.stdapp(my_graph, “Multi Scope”, nstatus=1)
app.MainLoop()

if name == ‘main’:
main ()

Diana I. wrote:

I tryed your code, but it’s not working. I get a lot of errors.
This is the complete program i have written(I’ve modified the
multi-fft.py file with the lines you wrote me).
Can you explain what this line does:
rx_subdev = self.rx_src.db[0]+self.rx_src.db[1]? The numbers 0 and 1
refer to inputs from the same Basic RX or to different Basix Rx boards?
I have connected only one Basic Rx.
Thank you very much for your help,
Diana

#!/usr/bin/env python
from gnuradio import gr, gru, eng_notation
from gnuradio import usrp
from gnuradio.eng_option import eng_option
from gnuradio import eng_notation
from gnuradio import optfir
from optparse import OptionParser
from gnuradio.wxgui import stdgui, fftsink2, waterfallsink, scopesink,
form, slider
import wx
from usrpm import usrp_dbid
import time
import os.path
import sys
class my_graph(stdgui.gui_flow_graph):

def __init__(self, frame, panel, vbox, argv):
    stdgui.gui_flow_graph.__init__(self)

    self.frame = frame
    self.panel = panel
usage="%prog: [options]"
    parser = OptionParser (option_class=eng_option,usage=usage)
    #parser.add_option("-S", "--subdev", type="subdev", default=(0, 

None),
# help=“select USRP Rx side A or B
(default=A)”)
parser.add_option("-d", “–decim”, type=“int”, default=128,
help=“set fgpa decimation rate to DECIM
[default=%default]”)
parser.add_option("-f", “–freq”, type=“eng_float”,
default=10.7e6,
help=“set frequency to FREQ
[default=%default])”, metavar=“FREQ”)
parser.add_option("-g", “–gain”, type=“eng_float”, default=0,
help=“set gain in dB [default=%default]”)
parser.add_option("-F", “–filter”, action=“store_true”,
default=True,
help=“Enable channel filter”)
(options, args) = parser.parse_args()

    if len(args) != 1:
        parser.print_help()
        raise SystemExit,1

n_chans = 2
fftsize_N=512
if options.filter:
        sw_decim = 4
    else:
        sw_decim = 1

self.rx_src = usrp.source_c()

if self.rx_src.nddc() < n_chans:
        sys.stderr.write('This code requires an FPGA build with %d 

DDCs. This FPGA has only %d.\n’ % (n_chans, self.rx_src.nddc()))
raise SystemExit,1

if not self.rx_src.set_nchannels(n_chans):
        sys.stderr.write('set_nchannels(%d) failed\n' % (n_chans,))
        raise SystemExit,1

    rx_rate = self.rx_src.adc_freq() / self.rx_src.decim_rate()
    print "USB data rate   = %s" % 

(eng_notation.num_to_str(rx_rate),)
print “Scope data rate = %s” %
(eng_notation.num_to_str(rx_rate/sw_decim),)

rx_subdev = self.rx_src.db[0]+self.rx_src.db[1]

self.rx_src.set_mux(gru.hexint(0xf1f0f3f2))

deinterleaver = gr.deinterleave(gr.sizeof_gr_complex)

for i in range(nchan):
       scope = fftsink.fft_sink_c(self, panel, 

sample_rate=input_rate/sw_decim,
title=“Input %d” % (i,),
ref_level=80, y_per_div=20)
vbox.Add(scope.win, 10, wx.EXPAND)
for i in range(n_chans)
self.rx_src_fft0 =
fftsink2.fft_sink_c(panel,title=“ChRXB-A”,fft_size=fftsize_N,sample_rate=rx_rate/sw_decim)
vbox.Add(self.rx_src_fft0.win,10,wx.EXPAND)
for i in range(n_chans)
self.rx_src_fft1 =
fftsink2.fft_sink_c(panel,title=“ChRXB-B”,fft_size=fftsize_N,sample_rate=rx_rate/sw_decim)
vbox.Add(self.rx_src_fft1.win,10,wx.EXPAND)

self.connect(self.rx_src,deinterleaver)

self.connect((deinterleaver,0),self.rx_src_fft0)
self.connect((deinterleaver,1),self.rx_src_fft1)



chan_filt_coeffs = optfir.low_pass (1,           # gain
                                        rx_rate,  # sampling rate
                                        80e3,        # passband 

cutoff
115e3, # stopband
cutoff
0.1, # passband
ripple
60) # stopband
attenuation
#print len(chan_filt_coeffs)

self.set_gain(options.gain)
    self.set_freq(options.freq)

def set_gain(self, gain):
    for i in range(len(self.subdev)):
        self.rx_subdev[i].set_gain(gain)

#Tune the receiver
def set_freq(self, target_freq):
ok = True
for i in range(len(rx_subdev)):
r = self.rx_src.tune(rx_subdev[i]._which, i, rx_subdev[i],
target_freq)
if not r:
ok = False
print “set_freq: failed to set subdev[%d] freq to %f” %
(i, target_freq)

    return ok

def main ():
app = stdgui.stdapp(my_graph, “Multi Scope”, nstatus=1)
app.MainLoop()

if name == ‘main’:
main ()
Karthik Vijayraghavan wrote:

On Wed, Apr 23, 2008 at 5:38 AM, Diana I. [email protected]
wrote:

Diana

Posted via http://www.ruby-forum.com/.

I recently finished doing exactly this. The following code works for
me. I modified this from the multi_fft example file.

n_chans = 2;
self.rx_src = usrp.source_c()
rx_subdev = self.rx_src.db[0]+self.rx_src.db[1]
self.rx_src.set_mux(gru.hexint(0xf1f0f3f2))
deinterleaver = gr.deinterleave(gr.sizeof_gr_complex)
self.rx_src_fft0 = fftsink2.fft_sink_c(panel,title=“Ch
RXB-A”,fft_size=fftsize_N,sample_rate=rx_rate/sw_decim)
self.rx_src_fft0 = fftsink2.fft_sink_c(panel,title=“Ch
RXB-B”,fft_size=fftsize_N,sample_rate=rx_rate/sw_decim)

self.connect(self.rx_src,deinterleaver)
self.connect((deinterleaver,0),self.rx_src_fft0)
self.connect((deinterleaver,1),self.rx_src_fft1)
vbox.Add(self.rx_src_fft0.win,10,wx.EXPAND)
vbox.Add(self.rx_src_fft1.win,10,wx.EXPAND)

#Tune the receiver
for i in range(len(rx_subdev)):
r = self.rx_src.tune(rx_subdev[i]._which,
rx_subdev[i],carrier_freq)
if not r:
print “set_freq: failed to set subdev[%d] freq to %f” %
(i, carrier_freq)

RXB-A appears on (deinterleaver,0) ad RXB-B appears on
(deinterleaver,1). If you are using RXA then you might want to replace
the mux value by something like 0xf3f2f1f0.

Regards,
Karthik


www.stanford.edu/~karthikv

On Thu, Apr 24, 2008 at 8:59 AM, Diana I. [email protected]
wrote:

File “./ruby.py”, line 79, in init
self.rx_src_fft0 =
fftsink.fft_sink_c(panel,title=“ChRXB-A”,fft_size=fftsize_N,sample_rate=rx_rate/sw_decim)
TypeError: init() takes at least 3 non-keyword arguments (2 given)

This is the real error.

Exception exceptions.ReferenceError: ‘weakly-referenced object no longer
exists’ in <bound method db_flexrf_2400_rx_mimo_b.del of
<gnuradio.db_flexrf_mimo.db_flexrf_2400_rx_mimo_b object at 0x891e28c>>
ignored

This is an artifact (not an error you have to worry about) of the
application not cleanly shutting down due to the prior exception. You
can ignore it.


Johnathan C.
Corgan Enterprises LLC
http://corganenterprises.com/

Diana I. wrote:

fftsink.fft_sink_c(panel,title=“ChRXB-A”,fft_size=fftsize_N,sample_rate=rx_rate/sw_decim)
TypeError: init() takes at least 3 non-keyword arguments (2 given)
Exception exceptions.ReferenceError: ‘weakly-referenced object no longer
exists’ in <bound method db_flexrf_2400_rx_mimo_b.del of
<gnuradio.db_flexrf_mimo.db_flexrf_2400_rx_mimo_b object at 0x891e28c>>
ignored
I think something is wrong with the gui…
Maybe you know what is that ReferenceError.

This seems to be your true error:
fftsink.fft_sink_c(panel,title=“ChRXB-A”,fft_size=fftsize_N,sample_rate=rx_rate/sw_decim)
TypeError: init() takes at least 3 non-keyword arguments (2 given)

  • George

George N. wrote:

Diana I. wrote:

I’m sorry, the correct code(which is not working) is the followig:

Tell us what your errors are.

  • George
    Errors:
    File “./ruby.py”, line 112, in
    main ()
    File “./ruby.py”, line 107, in main
    app = stdgui.stdapp(my_graph, “Multi Scope”, nstatus=1)
    File “/usr/lib/python2.5/site-packages/gnuradio/wxgui/stdgui.py”, line
    36, in init
    wx.App.init (self, redirect=False)
    File
    “/usr/lib/python2.5/site-packages/wx-2.6-gtk2-unicode/wx/_core.py”, line
    7700, in init
    self._BootstrapApp()
    File
    “/usr/lib/python2.5/site-packages/wx-2.6-gtk2-unicode/wx/_core.py”, line
    7352, in _BootstrapApp
    return core.PyApp__BootstrapApp(*args, **kwargs)
    File “/usr/lib/python2.5/site-packages/gnuradio/wxgui/stdgui.py”, line
    39, in OnInit
    frame = stdframe (self.flow_graph_maker, self.title, self._nstatus)
    File “/usr/lib/python2.5/site-packages/gnuradio/wxgui/stdgui.py”, line
    60, in init
    self.panel = stdpanel (self, self, flow_graph_maker)
    File “/usr/lib/python2.5/site-packages/gnuradio/wxgui/stdgui.py”, line
    81, in init
    self.fg = flow_graph_maker (frame, self, vbox, sys.argv)
    File “./ruby.py”, line 79, in init
    self.rx_src_fft0 =
    fftsink.fft_sink_c(panel,title=“ChRXB-A”,fft_size=fftsize_N,sample_rate=rx_rate/sw_decim)
    TypeError: init() takes at least 3 non-keyword arguments (2 given)
    Exception exceptions.ReferenceError: ‘weakly-referenced object no longer
    exists’ in <bound method db_flexrf_2400_rx_mimo_b.del of
    <gnuradio.db_flexrf_mimo.db_flexrf_2400_rx_mimo_b object at 0x891e28c>>
    ignored
    I think something is wrong with the gui…
    Maybe you know what is that ReferenceError.
    Thanks a lot,
    Diana

On Thu, Apr 24, 2008 at 9:04 AM, George N. [email protected] wrote:

ignored

  • George

My code uses fftsink2 whereas you are using fftsink. I was a little
bit lazy when I posted the lines relating to fftsink2 since they were
kind of long. Here is the full line

self.rx_src_fft0 = fftsink2.fft_sink_c(panel,title=“ChRXB-A”,
y_per_div=25, ref_level=fftreflev, average=False, fft_size=fftsize_N,
sample_rate=rx_rate/sw_decim)

similarly for the other channel. make sure you have “import fftsink2”
at the top as well. Here is a link to the file that works for me
http://www.stanford.edu/~karthikv/multichannel.py

Karthik

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs