# uncomment the line below to include support for Pi SDR
#PI_SDR_INCLUDE=PI_SDR
+# uncomment the line below to include MIDI support
+#MIDI_INCLUDE=MIDI
+
#uncomment the line below for the platform being compiled on (actually not used)
UNAME_N=raspberrypi
#UNAME_N=odroid
# uncomment the line below for various debug facilities
#DEBUG_OPTION=-D DEBUG
+ifeq ($(MIDI_INCLUDE),MIDI)
+MIDI_OPTIONS=-D MIDI
+MIDI_SOURCES= alsa_midi.c midi2.c midi3.c
+MIDI_HEADERS= midi.h
+MIDI_OBJS= alsa_midi.o midi2.o midi3.o
+MIDI_LIBS= -lasound
+endif
+
ifeq ($(PURESIGNAL_INCLUDE),PURESIGNAL)
PURESIGNAL_OPTIONS=-D PURESIGNAL
PURESIGNAL_SOURCES= \
AUDIO_LIBS=-lasound
#AUDIO_LIBS=-lsoundio
-OPTIONS=-g -Wno-deprecated-declarations $(PURESIGNAL_OPTIONS) $(REMOTE_OPTIONS) $(USBOZY_OPTIONS) $(I2C_OPTIONS) $(GPIO_OPTIONS) $(LIMESDR_OPTIONS) \
- $(FREEDV_OPTIONS) $(LOCALCW_OPTIONS) $(RADIOBERRY_OPTIONS) $(PI_SDR_OPTIONS) $(PSK_OPTIONS) $(STEMLAB_OPTIONS) $(STEMLAB_FIX_OPTION) \
- -D GIT_DATE='"$(GIT_DATE)"' -D GIT_VERSION='"$(GIT_VERSION)"' $(DEBUG_OPTION) -O3
+OPTIONS=-g -Wno-deprecated-declarations $(MIDI_OPTIONS) $(PURESIGNAL_OPTIONS) $(REMOTE_OPTIONS) $(USBOZY_OPTIONS) \
+ $(I2C_OPTIONS) $(GPIO_OPTIONS) $(LIMESDR_OPTIONS) $(FREEDV_OPTIONS) $(LOCALCW_OPTIONS) $(RADIOBERRY_OPTIONS) \
+ $(PI_SDR_OPTIONS) $(PSK_OPTIONS) $(STEMLAB_OPTIONS) $(STEMLAB_FIX_OPTION) \
+ -D GIT_DATE='"$(GIT_DATE)"' -D GIT_VERSION='"$(GIT_VERSION)"' $(DEBUG_OPTION) -O3
-LIBS=-lrt -lm -lwdsp -lpthread $(AUDIO_LIBS) $(USBOZY_LIBS) $(PSKLIBS) $(GTKLIBS) $(GPIO_LIBS) $(SOAPYSDRLIBS) $(FREEDVLIBS) $(STEMLAB_LIBS)
+LIBS=-lrt -lm -lwdsp -lpthread $(AUDIO_LIBS) $(USBOZY_LIBS) $(PSKLIBS) $(GTKLIBS) $(GPIO_LIBS) $(SOAPYSDRLIBS) $(FREEDVLIBS) $(STEMLAB_LIBS) $(MIDI_LIBS)
INCLUDES=$(GTKINCLUDES)
COMPILE=$(CC) $(OPTIONS) $(INCLUDES)
error_handler.o \
cwramp.o
-$(PROGRAM): $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) $(LOCALCW_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(PSK_OBJS) $(PURESIGNAL_OBJS) $(STEMLAB_OBJS)
- $(LINK) -o $(PROGRAM) $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) $(LOCALCW_OBJS) $(PSK_OBJS) $(PURESIGNAL_OBJS) $(STEMLAB_OBJS) $(LIBS)
-
-all: prebuild $(PROGRAM) $(HEADERS) $(REMOTE_HEADERS) $(USBOZY_HEADERS) $(LIMESDR_HEADERS) $(FREEDV_HEADERS) $(LOCALCW_HEADERS) $(I2C_HEADERS) $(GPIO_HEADERS) $(PSK_HEADERS) $(PURESIGNAL_HEADERS) $(STEMLAB_HEADERS) $(SOURCES) $(REMOTE_SOURCES) $(USBOZY_SOURCES) $(LIMESDR_SOURCES) $(FREEDV_SOURCES) $(I2C_SOURCES) $(GPIO_SOURCES) $(PSK_SOURCES) $(PURESIGNAL_SOURCES) $(STEMLAB_SOURCES)
+$(PROGRAM): $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) \
+ $(LOCALCW_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(PSK_OBJS) $(PURESIGNAL_OBJS) \
+ $(MIDI_OBJS) $(STEMLAB_OBJS)
+ $(LINK) -o $(PROGRAM) $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(I2C_OBJS) $(GPIO_OBJS) \
+ $(LIMESDR_OBJS) $(FREEDV_OBJS) $(LOCALCW_OBJS) $(PSK_OBJS) $(PURESIGNAL_OBJS) \
+ $(MIDI_OBJS) $(STEMLAB_OBJS) $(LIBS)
+
+all: prebuild $(PROGRAM) $(HEADERS) $(REMOTE_HEADERS) $(USBOZY_HEADERS) $(LIMESDR_HEADERS) \
+ $(FREEDV_HEADERS) $(LOCALCW_HEADERS) $(I2C_HEADERS) $(GPIO_HEADERS) $(PSK_HEADERS) \
+ $(PURESIGNAL_HEADERS) $(MIDI_HEADERS) $(STEMLAB_HEADERS) $(SOURCES) $(REMOTE_SOURCES) \
+ $(USBOZY_SOURCES) $(LIMESDR_SOURCES) $(FREEDV_SOURCES) $(I2C_SOURCES) $(GPIO_SOURCES) \
+ $(PSK_SOURCES) $(PURESIGNAL_SOURCES) $(MIDI_SOURCES)$(STEMLAB_SOURCES)
prebuild:
rm -f version.o
--- /dev/null
+/*
+ * MIDI support for pihpsdr
+ * (C) Christoph van Wullen, DL1YCF.
+ *
+ * This is the "Layer-1" for ALSA-MIDI (Linux)
+ * For further comments see file mac_midi.c
+ */
+
+/*
+ * ALSA: MIDI devices are sub-devices to sound cards.
+ * Therefore we have to loop through the sound cards
+ * and then, for each sound card, through the
+ * sub-devices until we have found "our" MIDI
+ * input device.
+ *
+ * The procedure how to find and talk with
+ * a MIDI device is taken from the sample
+ * program amidi.c in alsautils.
+ */
+
+#include "midi.h"
+
+#ifndef __APPLE__
+
+#include <pthread.h>
+#include <alsa/asoundlib.h>
+
+static pthread_t midi_thread_id;
+static void* midi_thread(void *);
+
+static char portname[64];
+
+static enum {
+ STATE_SKIP, // skip bytes
+ STATE_ARG1, // one arg byte to come
+ STATE_ARG2, // two arg bytes to come
+} state=STATE_SKIP;
+
+static enum {
+ CMD_NOTEON,
+ CMD_NOTEOFF,
+ CMD_CTRL,
+ CMD_PITCH,
+} command;
+
+static void *midi_thread(void *arg) {
+ int ret;
+ snd_rawmidi_t *input;
+ int npfds;
+ struct pollfd *pfds;
+ unsigned char buf[32];
+ unsigned char byte;
+ unsigned short revents;
+ int i;
+ int chan,arg1,arg2;
+
+ if ((ret = snd_rawmidi_open(&input, NULL, portname, SND_RAWMIDI_NONBLOCK)) < 0) {
+ fprintf(stderr,"cannot open port \"%s\": %s\n", portname, snd_strerror(ret));
+ return NULL;
+ }
+ snd_rawmidi_read(input, NULL, 0); /* trigger reading */
+
+ npfds = snd_rawmidi_poll_descriptors_count(input);
+ pfds = alloca(npfds * sizeof(struct pollfd));
+ snd_rawmidi_poll_descriptors(input, pfds, npfds);
+ for (;;) {
+ ret = poll(pfds, npfds, 250);
+ if (ret < 0) {
+ fprintf(stderr,"poll failed: %s\n", strerror(errno));
+ // Do not give up, but also do not fire too rapidly
+ usleep(250000);
+ }
+ if (ret <= 0) continue; // nothing arrived, do next poll()
+ if ((ret = snd_rawmidi_poll_descriptors_revents(input, pfds, npfds, &revents)) < 0) {
+ fprintf(stderr,"cannot get poll events: %s\n", snd_strerror(errno));
+ continue;
+ }
+ if (revents & (POLLERR | POLLHUP)) continue;
+ if (!(revents & POLLIN)) continue;
+ // something has arrived
+ ret = snd_rawmidi_read(input, buf, 64);
+ if (ret == 0) continue;
+ if (ret < 0) {
+ fprintf(stderr,"cannot read from port \"%s\": %s\n", portname, snd_strerror(ret));
+ continue;
+ }
+ // process bytes in buffer. Since they no not necessarily form complete messages
+ // we need a state machine here.
+ for (i=0; i< ret; i++) {
+ byte=buf[i];
+ switch (state) {
+ case STATE_SKIP:
+ chan=byte & 0x0F;
+ switch (byte & 0xF0) {
+ case 0x80: // Note-OFF command
+ command=CMD_NOTEOFF;
+ state=STATE_ARG2;
+ break;
+ case 0x90: // Note-ON command
+ command=CMD_NOTEON;
+ state=STATE_ARG2;
+ break;
+ case 0xB0: // Controller Change
+ command=CMD_CTRL;
+ state=STATE_ARG2;
+ break;
+ case 0xE0: // Pitch Bend
+ command=CMD_PITCH;
+ state=STATE_ARG2;
+ break;
+ case 0xA0: // Polyphonic Pressure
+ case 0xC0: // Program change
+ case 0xD0: // Channel pressure
+ case 0xF0: // System Message: continue waiting for bit7 set
+ default: // Remain in STATE_SKIP until bit7 is set
+ break;
+ }
+ break;
+ case STATE_ARG2:
+ arg1=byte;
+ state=STATE_ARG1;
+ break;
+ case STATE_ARG1:
+ arg2=byte;
+ // We have a command!
+ switch (command) {
+ case CMD_NOTEON:
+ NewMidiEvent(MIDI_NOTE, chan, arg1, 1);
+ break;
+ case CMD_NOTEOFF:
+ NewMidiEvent(MIDI_NOTE, chan, arg1, 0);
+ break;
+ case CMD_CTRL:
+ NewMidiEvent(MIDI_CTRL, chan, arg1, arg2);
+ break;
+ case CMD_PITCH:
+ NewMidiEvent(MIDI_PITCH, chan, 0, arg1+128*arg2);
+ break;
+ }
+ state=STATE_SKIP;
+ break;
+ }
+ }
+ }
+}
+
+void register_midi_device(char *myname) {
+
+ int mylen=strlen(myname);
+ snd_ctl_t *ctl;
+ snd_rawmidi_info_t *info;
+ int card, device, subs, sub, ret;
+ const char *devnam, *subnam;
+ int found=0;
+ char name[64];
+
+ card=-1;
+ if ((ret = snd_card_next(&card)) < 0) {
+ fprintf(stderr,"cannot determine card number: %s\n", snd_strerror(ret));
+ return;
+ }
+ while (card >= 0) {
+ fprintf(stderr,"Found Sound Card=%d\n",card);
+ sprintf(name,"hw:%d", card);
+ if ((ret = snd_ctl_open(&ctl, name, 0)) < 0) {
+ fprintf(stderr,"cannot open control for card %d: %s\n", card, snd_strerror(ret));
+ return;
+ }
+ device = -1;
+ // loop through devices of the card
+ for (;;) {
+ if ((ret = snd_ctl_rawmidi_next_device(ctl, &device)) < 0) {
+ fprintf(stderr,"cannot determine device number: %s\n", snd_strerror(ret));
+ break;
+ }
+ if (device < 0) break;
+ fprintf(stderr,"Found Device=%d on Card=%d\n", device, card);
+ // found sub-device
+ snd_rawmidi_info_alloca(&info);
+ snd_rawmidi_info_set_device(info, device);
+ snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_INPUT);
+ ret = snd_ctl_rawmidi_info(ctl, info);
+ if (ret >= 0) {
+ subs = snd_rawmidi_info_get_subdevices_count(info);
+ } else {
+ subs = 0;
+ }
+ fprintf(stderr,"Number of MIDI input devices: %d\n", subs);
+ if (!subs) break;
+ // subs: number of sub-devices to device on card
+ for (sub = 0; sub < subs; ++sub) {
+ snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_INPUT);
+ snd_rawmidi_info_set_subdevice(info, sub);
+ ret = snd_ctl_rawmidi_info(ctl, info);
+ if (ret < 0) {
+ fprintf(stderr,"cannot get rawmidi information %d:%d:%d: %s\n",
+ card, device, sub, snd_strerror(ret));
+ break;
+ }
+ if (found) break;
+ devnam = snd_rawmidi_info_get_name(info);
+ subnam = snd_rawmidi_info_get_subdevice_name(info);
+ // If there is only one sub-device and it has no name, we use
+ // devnam for comparison and make a portname of form "hw:x,y",
+ // else we use subnam for comparison and make a portname of form "hw:x,y,z".
+ if (sub == 0 && subnam[0] == '\0') {
+ sprintf(portname,"hw:%d,%d", card, device);
+ } else {
+ sprintf(portname,"hw:%d,%d,%d", card, device, sub);
+ devnam=subnam;
+ }
+ if (!strncmp(myname, devnam, mylen)) {
+ found=1;
+ fprintf(stderr,"MIDI device %s selected (PortName=%s)\n", devnam, portname);
+ } else {
+ fprintf(stderr,"MIDI device found BUT NOT SELECTED: %s\n", devnam);
+ }
+ }
+ }
+ snd_ctl_close(ctl);
+ // next card
+ if ((ret = snd_card_next(&card)) < 0) {
+ fprintf(stderr,"cannot determine card number: %s\n", snd_strerror(ret));
+ break;
+ }
+ }
+ if (!found) {
+ fprintf(stderr,"MIDI device %s NOT FOUND!\n", myname);
+ }
+ // Found our MIDI input device. Spawn off a thread reading data
+ ret = pthread_create(&midi_thread_id, NULL, midi_thread, NULL);
+ if (ret < 0) {
+ fprintf(stderr,"Failed to create MIDI read thread\n");
+ }
+}
+#endif
// We process *all* data but only generate calls to layer-2 for Note On/Off
// and ControllerChange events.
//
+
+//
+// Although MacOS does all the grouping of MIDI bytes into commands for us,
+// we use here the same state machine as we have in the ALSA MIDI implementation
+// That is, we look at each MIDI byte separately
+//
+
+static enum {
+ STATE_SKIP, // skip bytes until command bit is set
+ STATE_ARG1, // one arg byte to come
+ STATE_ARG2, // two arg bytes to come
+} state=STATE_SKIP;
+
+static enum {
+ CMD_NOTEON,
+ CMD_NOTEOFF,
+ CMD_CTRL,
+ CMD_PITCH,
+} command;
+
static void ReadMIDIdevice(const MIDIPacketList *pktlist, void *refCon, void *connRefCon) {
- int i,j,k,command,chan;
+ int i,j,k,byte,chan,arg1,arg2;
MIDIPacket *packet = (MIDIPacket *)pktlist->packet;
// loop through all packets in the current list
for (j=0; j < pktlist->numPackets; ++j) {
- if (packet->length > 0) {
- for ( i = 0; i<(packet->length); ) {
- command=packet->data[i];
- if ((command & 128) != 128) continue;
-
- chan = command & 0x0F;
-
- switch (command & 0xF0) {
- case 0x80: // Note off
- NewMidiEvent(MIDI_NOTE, chan, packet->data[i+1], 0);
- //fprintf(stderr,"NOTE OFF: Note=%d Chan=%d Vel=%d\n", packet->data[i+1], chan, packet->data[i+2]);
- i +=3;
- break;
- case 0x90: // Note on
- NewMidiEvent(MIDI_NOTE, chan, packet->data[i+1], 1);
- //fprintf(stderr,"NOTE ON : Note=%d Chan=%d Vel=%d\n", packet->data[i+1], chan, packet->data[i+2]);
- i +=3;
- break;
- case 0xA0: // Polyph. Press.
- fprintf(stderr,"PolPress: Note=%d Chan=%d Prs=%d\n", packet->data[i+1], chan, packet->data[i+2]);
- i +=3;
- break;
- case 0xB0: // Control change
- NewMidiEvent(MIDI_CTRL, chan, packet->data[i+1], packet->data[i+2]);
- //fprintf(stderr,"CtlChang: Ctrl=%d Chan=%d Val=%d\n", packet->data[i+1], chan, packet->data[i+2]);
- i +=3;
- break;
- case 0xC0: // Program change
- fprintf(stderr,"PgmChang: Prog=%d Chan=%d\n", packet->data[i+1], chan);
- i +=2;
- break;
- case 0xD0: // Channel Pressure
- fprintf(stderr, "ChanPres: Pres=%d Chan=%d\n", packet->data[i+1], chan);
- i +=2;
- break;
- case 0xE0: // Pitch Bend
- NewMidiEvent(MIDI_PITCH, chan, 0, packet->data[i+1] + 128*packet->data[i+2]);
- //fprintf(stderr,"Pitch : val =%d Chan=%d\n", packet->data[i+1] + 128*packet->data[i+2], chan);
- i +=3;
- break;
- case 0xF0:
- fprintf(stderr, "System : %x", command);
- while ((command = (packet->data[++i]) & 128) != 128) {
- fprintf(stderr," %x",command);
- }
- fprintf(stderr,"\n");
- break;
- }
- } // i-loop through the packet
- packet = MIDIPacketNext(packet);
- } // if packet length > 1
+ for (i=0; i<packet->length; i++) {
+ byte=packet->data[i];
+ switch (state) {
+ case STATE_SKIP:
+ chan=byte & 0x0F;
+ switch (byte & 0xF0) {
+ case 0x80: // Note-OFF command
+ command=CMD_NOTEOFF;
+ state=STATE_ARG2;
+ break;
+ case 0x90: // Note-ON command
+ command=CMD_NOTEON;
+ state=STATE_ARG2;
+ break;
+ case 0xB0: // Controller Change
+ command=CMD_CTRL;
+ state=STATE_ARG2;
+ break;
+ case 0xE0: // Pitch Bend
+ command=CMD_PITCH;
+ state=STATE_ARG2;
+ break;
+ case 0xA0: // Polyphonic Pressure: skip args
+ case 0xC0: // Program change: skip args
+ case 0xD0: // Channel pressure: skip args
+ case 0xF0: // System Message: skip args
+ default: // Remain in STATE_SKIP until "interesting" command seen
+ break;
+ }
+ break;
+ case STATE_ARG2: // store byte as first argument
+ arg1=byte;
+ state=STATE_ARG1;
+ break;
+ case STATE_ARG1: // store byte as second argument, process command
+ arg2=byte;
+ // We have a command!
+ switch (command) {
+ case CMD_NOTEON:
+ NewMidiEvent(MIDI_NOTE, chan, arg1, 1);
+ break;
+ case CMD_NOTEOFF:
+ NewMidiEvent(MIDI_NOTE, chan, arg1, 0);
+ break;
+ case CMD_CTRL:
+ NewMidiEvent(MIDI_CTRL, chan, arg1, arg2);
+ break;
+ case CMD_PITCH:
+ NewMidiEvent(MIDI_PITCH, chan, 0, arg1+128*arg2);
+ break;
+ }
+ state=STATE_SKIP;
+ break;
+ }
+ } // i-loop through the packet
+ packet = MIDIPacketNext(packet);
} // j-loop through the list of packets
}
static GtkWidget *discovery_dialog;
-#ifdef __APPLE__
-static sem_t *wisdom_sem;
-#else
-static sem_t wisdom_sem;
-#endif
-
static GdkCursor *cursor_arrow;
static GdkCursor *cursor_watch;
}
static pthread_t wisdom_thread_id;
+static int wisdom_running=0;
static void* wisdom_thread(void *arg) {
- fprintf(stderr,"Securing wisdom file in directory: %s\n", (char *)arg);
- status_text("Creating FFTW Wisdom file ...");
WDSPwisdom ((char *)arg);
-#ifdef __APPLE__
- sem_post(wisdom_sem);
-#else
- sem_post(&wisdom_sem);
-#endif
+ wisdom_running=0;
return NULL;
}
static int init(void *data) {
char *res;
char wisdom_directory[1024];
- char wisdom_file[1024];
int rc;
fprintf(stderr,"init\n");
// Let WDSP (via FFTW) check for wisdom file in current dir
// If there is one, the "wisdom thread" takes no time
// Depending on the WDSP version, the file is wdspWisdom or wdspWisdom00.
+ // sem_trywait() is not elegant, replaced this with wisdom_running variable.
//
res=getcwd(wisdom_directory, sizeof(wisdom_directory));
strcpy(&wisdom_directory[strlen(wisdom_directory)],"/");
- strcpy(wisdom_file,wisdom_directory);
-#ifdef __APPLE__
- wisdom_sem=sem_open("WISDOM", O_CREAT, 0700, 0);
-#else
- rc=sem_init(&wisdom_sem, 0, 0);
-#endif
- rc=pthread_create(&wisdom_thread_id, NULL, wisdom_thread, (void *)wisdom_directory);
-#ifdef __APPLE__
- while(sem_trywait(wisdom_sem)<0) {
-#else
- while(sem_trywait(&wisdom_sem)<0) {
-#endif
- status_text("WDSP wisdom done.");
- while (gtk_events_pending ())
- gtk_main_iteration ();
+ fprintf(stderr,"Securing wisdom file in directory: %s\n", wisdom_directory);
+ status_text("Creating FFTW Wisdom file ...");
+ wisdom_running=1;
+ rc=pthread_create(&wisdom_thread_id, NULL, wisdom_thread, wisdom_directory);
+ while (wisdom_running) {
+ // wait for the wisdom thread to complete, meanwhile
+ // handling any GTK events.
usleep(100000); // 100ms
+ while (gtk_events_pending ()) {
+ gtk_main_iteration ();
+ }
}
g_idle_add(ext_discovery,NULL);
# The key is suitable for radios with a step (ALEX) attenuator, the wheel
# fits best for radios with a programmable attenuator (0-31 dB)
#
-DEVICE=CMD PL 1
+DEVICE=CMD PL-1
CTRL=31 WHEEL THR=59 61 63 65 67 69 ACTION=VFO # Big wheel: : main VFO knob
PITCH ACTION=AFGAIN # Big slider : AF gain
KEY=16 ACTION=PREAMP # Key 1 : Cycle through Preamp settings
if (cp) *cp=0; // ignore trailing comment
if ((cp = strstr(zeile, "DEVICE="))) {
- // Delete trailing blanks and newlines
+ // Delete comments and trailing blanks
cq=cp+7;
- while (*cq != 0 && *cq != '\n' && *cq != ' ' && *cq != '\t') cq++;
+ while (*cq != 0 && *cq != '#') cq++;
+ *cq--=0;
+ while (cq > cp+7 && (*cq == ' ' || *cq == '\t')) cq--;
*cq=0;
register_midi_device(cp+7);
continue; // nothing more in this line
SetChannelState(transmitter->id,1,0);
tx_set_displaying(transmitter,1);
} else {
+ // switch to rx
SetChannelState(transmitter->id,0,1);
if(protocol==NEW_PROTOCOL) {
schedule_high_priority();
cairo_rectangle(cr, filter_left, 0.0, filter_right-filter_left, (double)display_height);
cairo_fill(cr);
- // plot the levels
- int V = (int)(tx->panadapter_high - tx->panadapter_low);
- int numSteps = V / 20;
- for (i = 1; i < numSteps; i++) {
- int num = tx->panadapter_high - i * 20;
- int y = (int)floor((tx->panadapter_high - num) * display_height / V);
- cairo_set_source_rgb (cr, 0.0, 1.0, 1.0);
- cairo_set_line_width(cr, 1.0);
- cairo_move_to(cr,0.0,(double)y);
- cairo_line_to(cr,(double)display_width,(double)y);
-
- cairo_set_source_rgb (cr, 0.0, 1.0, 1.0);
- cairo_select_font_face(cr, "FreeMono", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
- cairo_set_font_size(cr, 12);
+ // plot the levels 0, -20, 40, ... dBm (green line with label)
+ // also plot gray lines at -10, -30, -50, ... dBm (without label)
+ double dbm_per_line=(double)display_height/((double)tx->panadapter_high-(double)tx->panadapter_low);
+ cairo_set_source_rgb (cr, 0.00, 1.00, 1.00);
+ cairo_set_line_width(cr, 1.0);
+ cairo_select_font_face(cr, "FreeMono", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
+ cairo_set_font_size(cr, 12);
+
+ for(i=tx->panadapter_high;i>=tx->panadapter_low;i--) {
char v[32];
- sprintf(v,"%d dBm",num);
- cairo_move_to(cr, 1, (double)y);
- cairo_show_text(cr, v);
- cairo_stroke(cr);
+ if((abs(i)%10) ==0) {
+ double y = (double)(tx->panadapter_high-i)*dbm_per_line;
+ if ((abs(i) % 20) == 0) {
+ cairo_set_source_rgb (cr, 0.00, 1.00, 1.00);
+ cairo_move_to(cr,0.0,y);
+ cairo_line_to(cr,(double)display_width,y);
+
+ sprintf(v,"%d dBm",i);
+ cairo_move_to(cr, 1, y);
+ cairo_show_text(cr, v);
+ cairo_stroke(cr);
+ } else {
+ cairo_set_source_rgb (cr, 0.25, 0.25, 0.25);
+ cairo_move_to(cr,0.0,y);
+ cairo_line_to(cr,(double)display_width,y);
+ cairo_stroke(cr);
+ }
+ }
}
// plot frequency markers