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.