Saturday, November 7, 2009

Finny: A Linux Frontend for the RadioSHARK2

Finny is a Linux front-end for the RadioSHARK2 AM/FM usb receiver, intended for in-auto "carputer" or automobile-PC applications. It can also be used on home PCs and workstations, however. It is Qt4 based, with Gstreamer based audio capture, and some ALSA calls.

Finny is currently under development. Contact: oneil.code@gmail.com


Screenshots:



Its HID interface to the radioSHARK2 is largely based upon the execellent work of:
Michael Rolig
Hisaaki Shibata
Kawabe

The project is in the mid-to-late coding stages. I anticipate a first release at the end of 2009.

Finny was put together with the following objectives:
Simple. The Code should be as small as it possibly can be.
As much as possible, it should be based upon standard Qt widgets.
A person driving should be able to use it.
The buttons ought to be large and easily found.
Some small safety features ought to prevent the user from pressing "dangerous" buttons, like "erase preset" and "record."
The text ought to be large, friendly and easily readable.
The application ought to be usable when presented fullscreen on a small auto-pc screen of say 600x400.
Finny will not be a general media player.
It will only play back live audio from the RadioSHARK2 device.
Finny will be able to record live audio to mp3 format for playback on another application or device.

Currently, finny can do the following:
Control RadioSHARK2 fully, setting AM and FM frequencies.
Step (quickly or slowly) over frequencies.
Save presets, and load the presets at startup.
Play live captured AM/FM audio.
Record live captured AM/FM audio to mp3 format.
Visualize live audio using the Gstreamer "monoscope" plugin.


Saturday, October 31, 2009

Globdog DG-100 first release (0.5 beta)

I've just put the first primitive release of the GlobalSat DG-100 linux manager application "GlobDoG DG-100."

This is available here.

The release is as a primitive .deb package, only tested for Ubuntu 9.04 and 9.1.

When one downloads the package, one need only double-click it to start the installation procedure. I've checked it resolves all needed dependencies.

The application should then be available in the Applications-->Accessories menu.

Sunday, October 25, 2009

Radioshark2 Full Duplex code

I recently got a Radioshark2, and with the help on the web I was very easily able to get it running in Ubuntu 9. The following posts were very helpful:
Radioshark Control Link
Radioshark Control Link 2

After using the ./Shark2 control program to set a station, for live playback I use the command:

arecord -f dat -D hw:1,0 | aplay

It seems to me a basic GUI interface for the Radioshark for linux is lacking, so I did a little work on it over the past day. I basically cut and paste a lot of stuff together from these sources:

Alsa Tutorial 1
Alsa Tutorial 2

The result is a C++ class that I can use to play full-duplex (i.e. live captured and playback) sound off the Radioshark. It's still very basic (no proper error handling, not hardware independent etc,etc..) but it seems like a decent first step.


/* Use the newer ALSA API */
#define ALSA_PCM_NEW_HW_PARAMS_API
#include <alsa/asoundlib.h>

#include <iostream>
#include <string>

/*
compile: g++ AudioInterface.cpp `pkg-config --cflags --libs alsa` -o AudioInterface

*/
using namespace std;

class AudioInterface
{
public:
snd_pcm_t *hCapture;
snd_pcm_t *hPlayback;

//Hopefully both devices can share these
snd_pcm_hw_params_t *CaptureParams;//,*PlaybackParams;
snd_pcm_uframes_t frames;
snd_pcm_sw_params_t *Capture_sw_params;

char *buffer;
unsigned int period;
int buffer_size;

/*handle to callback*/
snd_async_handler_t *pcm_callback;

public:
unsigned int GetPeriod(void)
{
if(period > 0)
{
return period;
}else{
1;
}
};
static void OnPCMData(snd_async_handler_t *pcm_callback )
{

//cout<<" PCM DATA ARRIVED."<<endl;
AudioInterface* interface = (AudioInterface*) snd_async_handler_get_callback_private(pcm_callback);
if(!interface->hPlayback || !interface->hCapture)
{
return;
}

snd_pcm_t *pcm_handle = snd_async_handler_get_pcm(pcm_callback);
snd_pcm_sframes_t avail;
int rc = 0;

avail = snd_pcm_avail_update(interface->hCapture);
while ( avail >= interface->frames) {
//cout<<"loop ---"<<avail<<endl;
rc = snd_pcm_readi(interface->hCapture, interface->buffer,interface->frames);
if ( rc < 0) {
fprintf(stderr,"Read error: %s\n", snd_strerror(rc));
}
if ( rc != interface->frames ) {
fprintf(stderr,"Read error: read %i expected %i\n", rc, (int)interface->frames);
}else{
//cout<<"read:"<<rc<<endl;
}

rc = snd_pcm_writei(interface->hPlayback,interface->buffer,interface->frames);
if( rc < 0 )
{
fprintf(stderr,"Write Error: %s\n", snd_strerror(rc));
}
if ( rc == -EAGAIN){
fprintf(stderr,"Underrun: %s\n", snd_strerror(rc));
}else if( rc == -ESTRPIPE ){
fprintf(stderr,"Suspend: %s\n", snd_strerror(rc));
}else if( rc == -EPIPE ){
fprintf(stderr,"EPIPE: %s\n", snd_strerror(rc));
snd_pcm_prepare(interface->hPlayback);
}/*else if( rc != interface->frames){
fprintf(stderr,"Write error: written %i expected %i\n", rc, (int)interface->frames);
}*/

/* if (err < 0) {

if ( rc < 0) {
fprintf(stderr,"Write error: %s\n", snd_strerror(rc));
}
if ( rc != interface->frames ) {
fprintf(stderr,"Write error: written %i expected %i\n", rc, (int)interface->frames);
}else{
//cout<<"wrote:"<<rc<<endl;
}*/

avail = snd_pcm_avail_update(interface->hCapture);

}
//cout<<"UNHANDLED : "<<avail<<endl;

};
AudioInterface()
:hCapture(NULL)
,hPlayback(NULL)
//,PlaybackParams(NULL)
,CaptureParams(NULL)
,buffer(NULL)
,pcm_callback(NULL)
,Capture_sw_params(NULL)
,buffer_size(0)
{

};
~AudioInterface()
{

};

bool Open(const string& capture_dev,const string& output_dev)
{
int dir = 0;
unsigned int val = 0;
int rc = 0;

//Open the playback device (ought to be "default")
rc = snd_pcm_open(&hPlayback, output_dev.c_str(),
SND_PCM_STREAM_PLAYBACK, 0 );
if (rc < 0)
{
fprintf(stderr,
"unable to open pcm playback device: %s\n",
snd_strerror(rc));
return false;
}
//Open the capture device-- we'll have to look for it.
//For now we'll use hw:1,0
rc = snd_pcm_open(&hCapture, capture_dev.c_str(),
SND_PCM_STREAM_CAPTURE , 0 );
if (rc < 0)
{
fprintf(stderr,
"unable to open pcm capture device: %s\n",
snd_strerror(rc));
return false;
}
/* Allocate a hardware parameters object. */
snd_pcm_hw_params_alloca(&CaptureParams);
//snd_pcm_hw_params_alloca(&PlaybackParams);

/* Fill it in with default values. */
snd_pcm_hw_params_any(hCapture, CaptureParams);
//snd_pcm_hw_params_any(hPlayback, PlaybackParams);

/* Set the desired hardware parameters. */

/* Interleaved mode */
snd_pcm_hw_params_set_access(hCapture, CaptureParams,
SND_PCM_ACCESS_RW_INTERLEAVED);
//snd_pcm_hw_params_set_access(hPlayback, PlaybackParams,
// SND_PCM_ACCESS_RW_INTERLEAVED);

/* Signed 16-bit little-endian format */
snd_pcm_hw_params_set_format(hCapture, CaptureParams,
SND_PCM_FORMAT_S16_LE);
//snd_pcm_hw_params_set_format(hPlayback, PlaybackParams,
// SND_PCM_FORMAT_S16_LE);

/* Two channels (stereo) */
snd_pcm_hw_params_set_channels(hCapture, CaptureParams, 2);
//snd_pcm_hw_params_set_channels(hPlayback, PlaybackParams, 2);

/* 44100 bits/second sampling rate (CD quality) */
val = 44100;
snd_pcm_hw_params_set_rate_near(hCapture, CaptureParams,
&val,0);
//snd_pcm_hw_params_set_rate_near(hPlayback, PlaybackParams,
// &val,0);

/* Set period size to 32 frames. */
frames = 32;
//if(snd_pcm_hw_params_test_period_time(hCapture,CaptureParams,frames,0)!=0)
//{
// cout<<"Can't handle 32 frames."<<endl;
//}
snd_pcm_hw_params_set_period_size_near(hCapture,
CaptureParams, &frames, &dir);
//snd_pcm_hw_params_set_period_size_near(hPlayback,
// PlaybackParams, &frames, &dir);


/* Use a buffer large enough to hold one period */
snd_pcm_hw_params_get_period_size(CaptureParams, &frames,0);
cout<<"capture frames :"<<frames<<endl;
//snd_pcm_hw_params_get_period_size(PlaybackParams, &frames,0);
//cout<<"playback frames:"<<frames<<endl;


/* We want to loop for 5 seconds */
snd_pcm_hw_params_get_period_time(CaptureParams,
&period, &dir);


cout<<"Period: "<<period<<endl;

buffer_size = frames*4*4 ; /* 2 bytes/sample, 2 channels */
cout<<"Frames :"<<frames<<" size: "<<buffer_size<<endl;
//buffer = (char *) malloc(size);
buffer = new char[buffer_size];

/* Write the parameters to the Capture driver */
rc = snd_pcm_hw_params(hCapture, CaptureParams);
if (rc < 0)
{
fprintf(stderr,
"unable to set hw parameters: %s\n",
snd_strerror(rc));
return false;
}

/* Write the parameters to the Playback driver */
rc = snd_pcm_hw_params(hPlayback, CaptureParams);
if (rc < 0)
{
fprintf(stderr,
"unable to set hw parameters: %s\n",
snd_strerror(rc));
return false;
}

/* setup the software parameters */
snd_pcm_sw_params_malloc (&Capture_sw_params);
snd_pcm_sw_params_current ( hCapture , Capture_sw_params);
snd_pcm_sw_params_set_start_threshold( hCapture , Capture_sw_params , buffer_size/2);
snd_pcm_sw_params_set_avail_min( hCapture , Capture_sw_params , frames );

snd_pcm_sw_params( hCapture , Capture_sw_params );


/* connect up our callback*/
snd_async_add_pcm_handler(&pcm_callback,hCapture,AudioInterface::OnPCMData,(void*)this);

/* Startup */
snd_pcm_start(hCapture);


return true;

};

void Close(void)
{
if(pcm_callback)
{
snd_async_del_handler(pcm_callback);
}
if(hCapture)
{
snd_pcm_drain(hCapture);
snd_pcm_close(hCapture);
}
if(hPlayback)
{
snd_pcm_drain(hPlayback);
snd_pcm_close(hPlayback);
}
//if(PlaybackParams)
///{
// snd_pcm_hw_params_free(PlaybackParams);
//}
if(CaptureParams)
{
//snd_pcm_hw_params_free(CaptureParams);
}
if( Capture_sw_params)
{
//snd_pcm_sw_params_free (Capture_sw_params);

}
delete [] buffer;
};

};

int main(int argc, char* argv[])
{

cout<<"AUDIO API TEST"<<endl;
cout<<"PROGRAM START"<<endl;

//Open audio interface
AudioInterface ai;
if(!ai.Open(string("hw:1,0"),string("default")))
{
cout<<"Failed to open audio interface. Exiting..."<<endl;
exit(-1);
}

//play for 5 seconds
unsigned int loops = 10000000 / ai.GetPeriod();
//cout<<"Will run for "<<loops<<" loops."<<endl;

while ( loops > 0 ) {
loops--;
sleep(1);


}

//close audio interface
ai.Close();

cout<<"PROGRAM COMPLETE"<<endl;
}

Tuesday, October 20, 2009

GlobDog-DG100

Begun work on QT based Linux manager for the GlobalSat DG100 GPS tracker. I was recently on vacation, and found myself using the GPSBabel utility to download my trackfiles off the device, but it didn't cut it. Started coding this manager "the GlobDog" while on vacation, and enjoyed it immensely, although finding accruate information about the communicatino protocol has proven difficult.
Could this mean that there are protocol differences between versions of the DG100 device?

Anticipate general code completion in about a week.