dvap comm is now through h/w register model in new CDVAPDongle class

pull/1/head
Tom Early 9 years ago
parent 8036b7eca1
commit 1ace589916

@ -0,0 +1,748 @@
/*
* Copyright 2017 by Thomas Early, AC2IE
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <termios.h>
#include <time.h>
#include <sys/file.h>
#include <atomic>
#include "DVAPDongle.h"
extern std::atomic<bool> keep_running;
CDVAPDongle::CDVAPDongle() : MAX_REPL_CNT(20u)
{
}
CDVAPDongle::~CDVAPDongle()
{
}
bool CDVAPDongle::Initialize(char *serialno, int frequency, int offset, int power, int squelch)
{
bool ok = false;
char device[128];
do {
for (int i = 0; i < 32; i++) {
sprintf(device, "/dev/ttyUSB%d", i);
if (access(device, R_OK | W_OK) != 0)
continue;
ok = OpenSerial(device);
if (!ok)
continue;
if (flock(serfd, LOCK_EX | LOCK_NB) != 0) {
close(serfd);
serfd = -1;
ok = false;
traceit("Device %s is already locked/used\n", device);
continue;
}
traceit("Device %s now locked for exclusive use\n", device);
ok = get_ser(device, serialno);
if (!ok) {
close(serfd);
serfd = -1;
continue;
}
break;
}
if (!ok)
break;
ok = get_name();
if (!ok)
break;
ok = get_fw();
if (!ok)
break;
ok = set_modu();
if (!ok)
break;
ok = set_mode();
if (!ok)
break;
ok = set_sql(squelch);
if (!ok)
break;
ok = set_pwr(power);
if (!ok)
break;
ok = set_off(offset);
if (!ok)
break;
ok = set_freq(frequency);
if (!ok)
break;
ok = start_dvap();
if (!ok)
break;
} while (false);
if (!ok) {
if (serfd != -1) {
Stop();
close(serfd);
serfd = -1;
}
return false;
}
return true;
}
REPLY_TYPE CDVAPDongle::get_reply(SDVAP_REGISTER &dr)
{
dr.header = dr.param.control = 0;
unsigned int off = 2;
int rc = read_from_dvp(&dr.header, 2);
if (rc == 0)
return RT_TIMEOUT;
if (rc != 2)
return RT_ERR;
uint16_t len = dr.header & 0x1fff;
if (len > 50) {
syncit();
return RT_TIMEOUT;
}
while (off < len) {
uint8_t *ptr = (uint8_t *)&dr;
rc = read_from_dvp(ptr + off, len - off);
if (rc < 0)
return RT_TIMEOUT;
if (rc > 0)
off += rc;
}
if (0x2007u==dr.header && 0x90u==dr.param.control)
return RT_STS;
else if (0xc012u==dr.header)
return RT_DAT;
else if (0xa02fu==dr.header)
return RT_HDR;
else if (0x602fu==dr.header)
return RT_HDR_ACK;
else if (0x2005u==dr.header && 0x118u==dr.param.control)
return RT_PTT;
else if (0x5u==dr.header && 0x18u==dr.param.control && 0x1==dr.param.byte)
return RT_START;
else if (0x5u==dr.header && 0x18u==dr.param.control && 0x0==dr.param.byte)
return RT_STOP;
else if (0x6u==dr.header && 0x400u==dr.param.control)
return RT_OFF;
else if (0x10u==dr.header && 0x1u==dr.param.control)
return RT_NAME;
else if (0xdu==dr.header && 0x2u==dr.param.control)
return RT_SER;
else if (0x7u==dr.header && 0x4u==dr.param.control && 0x1u==dr.param.ustr[0])
return RT_FW;
else if (0x8u==dr.header && 0x220u==dr.param.control)
return RT_FREQ;
else if (0xcu==dr.header && 0x230u==dr.param.control)
return RT_FREQ_LIMIT;
else if (0x5u==dr.header && 0x28u==dr.param.control && 0x1u==dr.param.ustr[0])
return RT_MODU;
else if (0x5u==dr.header && 0x2au==dr.param.control && 0x0u==dr.param.ustr[0])
return RT_MODE;
else if (0x6u==dr.header && 0x138u==dr.param.control)
return RT_PWR;
else if (0x5u== dr.header && 0x80u==dr.param.control)
return RT_SQL;
else {
syncit();
return RT_TIMEOUT;
}
/* It should never get here */
return RT_TIMEOUT;
}
void CDVAPDongle::syncit()
{
unsigned char data[7];
struct timeval tv;
fd_set fds;
short cnt = 0;
traceit("Starting syncing dvap\n");
memset(data, 0x00, 7);
dvapreg.header = 0x2007u;
dvapreg.param.control = 0x90u;
while (memcmp(data, &dvapreg, 4) != 0) {
FD_ZERO(&fds);
FD_SET(serfd, &fds);
tv.tv_sec = 0;
tv.tv_usec = 1000;
int n = select(serfd + 1, &fds, NULL, NULL, &tv);
if (n <= 0) {
cnt ++;
if (cnt > 100) {
traceit("dvap is not responding,...stopping\n");
keep_running = false;
return;
}
} else {
unsigned char c;
n = read_from_dvp(&c, 1);
if (n > 0) {
data[0] = data[1];
data[1] = data[2];
data[2] = data[3];
data[3] = data[4];
data[4] = data[5];
data[5] = data[6];
data[6] = c;
cnt = 0;
}
}
}
traceit("Stopping syncing dvap\n");
return;
}
bool CDVAPDongle::get_ser(char *dvp, char *dvap_serial_number)
{
unsigned cnt = 0;
REPLY_TYPE reply;
dvapreg.header = 0x2004u;
dvapreg.param.control = 0x2u;
int rc = write_to_dvp(&dvapreg, 4);
if (rc != 4) {
traceit("Failed to send request to get dvap serial#\n");
return false;
}
do {
usleep(5000);
reply = get_reply(dvapreg);
cnt ++;
if (cnt >= MAX_REPL_CNT) {
traceit("Reached max number of requests to receive dvap serial#\n");
return false;
}
} while (reply != RT_SER);
if (0 == strcmp(dvapreg.param.sstr, dvap_serial_number)) {
traceit("Using %s: %s, because serial number matches your dvap_rptr.cfg\n", dvp, dvap_serial_number);
return true;
}
traceit("Device %s has serial %s, but does not match your config value %s\n", dvp, dvapreg.param.sstr, dvap_serial_number);
return false;
}
bool CDVAPDongle::get_name()
{
unsigned cnt = 0;
REPLY_TYPE reply;
dvapreg.header = 0x2004u;
dvapreg.param.control = 0x1u;
int rc = write_to_dvp(&dvapreg, 4);
if (rc != 4) {
traceit("Failed to send request to get dvap name\n");
return false;
}
do {
usleep(5000);
reply = get_reply(dvapreg);
cnt ++;
if (cnt >= MAX_REPL_CNT) {
traceit("Reached max number of requests to receive dvap name\n");
return false;
}
} while (reply != RT_NAME);
if (0x10u!=dvapreg.header || 0x1u!=dvapreg.param.control || strncmp(dvapreg.param.sstr, "DVAP Dongle", 11)) {
traceit("Failed to receive dvap name, got %s\n", dvapreg.param.sstr);
return false;
}
traceit("Device name: %.*s\n", 11, dvapreg.param.sstr);
return true;
}
bool CDVAPDongle::get_fw()
{
unsigned cnt = 0;
REPLY_TYPE reply;
dvapreg.header = 0x2005u;
dvapreg.param.control = 0x4u;
dvapreg.param.ustr[0] = 0x1u;
int rc = write_to_dvp(&dvapreg, 5);
if (rc != 5) {
traceit("Failed to send request to get dvap fw\n");
return false;
}
do {
usleep(5000);
reply = get_reply(dvapreg);
cnt ++;
if (cnt >= MAX_REPL_CNT) {
traceit("Reached max number of requests to receive dvap fw\n");
return false;
}
} while (reply != RT_FW);
unsigned int ver = dvapreg.param.ustr[1] + 256 * dvapreg.param.ustr[2];
traceit("dvap fw ver: %u.%u\n", ver / 100, ver % 100);
return true;
}
bool CDVAPDongle::set_modu()
{
unsigned cnt = 0;
REPLY_TYPE reply;
dvapreg.header = 0x5u;
dvapreg.param.control = 0x28u;
dvapreg.param.ustr[0] = 0x1u;
int rc = write_to_dvp(&dvapreg, 5);
if (rc != 5) {
traceit("Failed to send request to set dvap modulation\n");
return false;
}
do {
usleep(5000);
reply = get_reply(dvapreg);
cnt ++;
if (cnt >= MAX_REPL_CNT) {
traceit("Reached max number of requests to set dvap modulation\n");
return false;
}
} while (reply != RT_MODU);
return true;
}
bool CDVAPDongle::set_mode()
{
unsigned cnt = 0;
REPLY_TYPE reply;
dvapreg.header = 0x5u;
dvapreg.param.control = 0x2au;
dvapreg.param.ustr[0] = 0x0u;
int rc = write_to_dvp(&dvapreg, 5);
if (rc != 5) {
traceit("Failed to send request to set dvap mode\n");
return false;
}
do {
usleep(5000);
reply = get_reply(dvapreg);
cnt ++;
if (cnt >= MAX_REPL_CNT) {
traceit("Reached max number of requests to set dvap mode\n");
return false;
}
} while (reply != RT_MODE);
return true;
}
bool CDVAPDongle::set_sql(int squelch)
{
unsigned cnt = 0;
REPLY_TYPE reply;
dvapreg.header = 0x5u;
dvapreg.param.control = 0x80u;
if (squelch < -128) {
traceit("Squelch setting of %d too small, resetting...\n", squelch);
squelch = -128;
} else if (squelch > -45) {
traceit("Squelch setting of %d too large, resetting...\n", squelch);
squelch = -45;
}
dvapreg.param.byte = (int8_t)squelch;
int rc = write_to_dvp(&dvapreg, 5);
if (rc != 5) {
traceit("Failed to send request to set dvap sql\n");
return false;
}
do {
usleep(5000);
reply = get_reply(dvapreg);
cnt ++;
if (cnt >= MAX_REPL_CNT) {
traceit("Reached max number of requests to set dvap sql\n");
return false;
}
} while (reply != RT_SQL);
traceit("DVAP squelch is %d dB\n", (int)dvapreg.param.byte);
return true;
}
bool CDVAPDongle::set_pwr(int power)
{
unsigned cnt = 0;
REPLY_TYPE reply;
dvapreg.header = 0x6u;
dvapreg.param.control = 0x138u;
if (power < -12) {
traceit("Power setting of %d is too low, resetting...\n", power);
power = -12;
} else if (power > 10) {
traceit("Power setting of %d is too high, resetting...\n", power);
power = 10;
}
dvapreg.param.word = (int16_t)power;
int rc = write_to_dvp(&dvapreg, 6);
if (rc != 6) {
traceit("Failed to send request to set dvap pwr\n");
return false;
}
do {
usleep(5000);
reply = get_reply(dvapreg);
cnt ++;
if (cnt >= MAX_REPL_CNT) {
traceit("Reached max number of requests to set dvap pwr\n");
return false;
}
} while (reply != RT_PWR);
traceit("DVAP power is %d dB\n", (int)dvapreg.param.word);
return true;
}
bool CDVAPDongle::set_off(int offset)
{
unsigned cnt = 0;
REPLY_TYPE reply;
dvapreg.header = 0x6u;
dvapreg.param.control = 0x400u;
if (offset < -2000) {
traceit("Offset of %d is too low, resetting...\n", offset);
offset = -2000;
} else if (offset > 2000) {
traceit("Offset of %d is too high, resetting...\n", offset);
offset = 2000;
}
dvapreg.param.word = (int16_t)offset;
int rc = write_to_dvp(&dvapreg, 6);
if (rc != 6) {
traceit("Failed to send request to set dvap offset\n");
return false;
}
do {
usleep(5000);
reply = get_reply(dvapreg);
cnt ++;
if (cnt >= MAX_REPL_CNT) {
traceit("Reached max number of requests to set dvap offset\n");
return false;
}
} while (reply != RT_OFF);
traceit("DVAP offset is %d Hz\n", (int)dvapreg.param.word);
return true;
}
bool CDVAPDongle::set_freq(int frequency)
{
unsigned cnt = 0;
REPLY_TYPE reply;
// first get the frequency limits
dvapreg.header = 0x2004u;
dvapreg.param.control = 0x230u;
int rc = write_to_dvp(&dvapreg, 4);
if (rc != 4) {
traceit("Failed to send request for frequency limits\n");
return false;
}
do {
usleep(5000);
reply = get_reply(dvapreg);
cnt++;
if (cnt >= MAX_REPL_CNT) {
traceit("Reached max number of requests for dvap frequency limits\n");
return false;
}
} while (reply != RT_FREQ_LIMIT);
traceit("DVAP Frequency limits are from %d to %d Hz\n", dvapreg.param.twod[0], dvapreg.param.twod[1]);
// okay, now we know the frequency limits, get on with the show...
if (frequency < dvapreg.param.twod[0]) {
traceit("Frequency of %d is too small, resetting...\n", frequency);
frequency = dvapreg.param.twod[0];
} else if (frequency > dvapreg.param.twod[1]) {
traceit("Frequency of %d is too large, resetting...\n", frequency);
frequency = dvapreg.param.twod[1];
}
cnt = 0;
dvapreg.header = 0x8u;
dvapreg.param.control = 0x220u;
dvapreg.param.dword = frequency;
rc = write_to_dvp(&dvapreg, 8);
if (rc != 8) {
traceit("Failed to send request to set dvap frequency\n");
return false;
}
do {
usleep(5000);
reply = get_reply(dvapreg);
cnt ++;
if (cnt >= MAX_REPL_CNT) {
traceit("Reached max number of requests to set dvap frequency\n");
return false;
}
} while (reply != RT_FREQ);
traceit("DVAP frequency is %d Hz\n", dvapreg.param.dword);
return true;
}
bool CDVAPDongle::start_dvap()
{
unsigned cnt = 0;
REPLY_TYPE reply;
dvapreg.header = 0x5u;
dvapreg.param.control = 0x18u;
dvapreg.param.byte = 0x1;
int rc = write_to_dvp(&dvapreg, 5);
if (rc != 5) {
traceit("Failed to send request to start the dvap dongle\n");
return false;
}
do {
usleep(5000);
reply = get_reply(dvapreg);
cnt ++;
if (cnt >= MAX_REPL_CNT) {
traceit("Reached max number of requests to start the dvap dongle\n");
return false;
}
} while (reply != RT_START);
return true;
}
void CDVAPDongle::SendRegister(SDVAP_REGISTER &dr)
{
unsigned int len = dr.header & 0x1fff;
write_to_dvp(&dr, len);
return;
}
int CDVAPDongle::write_to_dvp(const void *buffer, const unsigned int len)
{
unsigned int ptr = 0;
if (len == 0)
return 0;
uint8_t *buf = (uint8_t *)buffer;
while (ptr < len) {
ssize_t n = write(serfd, buf + ptr, len - ptr);
if (n < 0) {
traceit("Error %d writing to dvap, message=%s\n", errno, strerror(errno));
return -1;
}
if (n > 0)
ptr += n;
}
return len;
}
int CDVAPDongle::read_from_dvp(void *buffer, unsigned int len)
{
unsigned int off = 0;
fd_set fds;
int n;
struct timeval tv;
ssize_t temp_len;
uint8_t *buf = (uint8_t *)buffer;
if (len == 0)
return 0;
while (off < len) {
FD_ZERO(&fds);
FD_SET(serfd, &fds);
if (off == 0) {
tv.tv_sec = 0;
tv.tv_usec = 0;
n = select(serfd + 1, &fds, NULL, NULL, &tv);
if (n == 0)
return 0; // nothing to read from the dvap
} else
n = select(serfd + 1, &fds, NULL, NULL, NULL);
if (n < 0) {
traceit("select error=%d on dvap\n", errno);
return -1;
}
if (n > 0) {
temp_len = read(serfd, buf + off, len - off);
if (temp_len > 0)
off += temp_len;
}
}
return len;
}
bool CDVAPDongle::OpenSerial(char *device)
{
static termios t;
serfd = open(device, O_RDWR | O_NOCTTY | O_NDELAY, 0);
if (serfd < 0) {
traceit("Failed to open device [%s], error=%d, message=%s\n", device, errno, strerror(errno));
return false;
}
if (isatty(serfd) == 0) {
traceit("Device %s is not a tty device\n", device);
close(serfd);
serfd = -1;
return false;
}
if (tcgetattr(serfd, &t) < 0) {
traceit("tcgetattr failed for %s, error=%d, message-%s\n", device, errno, strerror(errno));
close(serfd);
serfd = -1;
return false;
}
t.c_lflag &= ~(ECHO | ECHOE | ICANON | IEXTEN | ISIG);
t.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON | IXOFF | IXANY);
t.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CRTSCTS);
t.c_cflag |= CS8;
t.c_oflag &= ~(OPOST);
t.c_cc[VMIN] = 0;
t.c_cc[VTIME] = 10;
cfsetospeed(&t, B230400);
cfsetispeed(&t, B230400);
if (tcsetattr(serfd, TCSANOW, &t) < 0) {
traceit("tcsetattr failed for %s, error=%dm message=%s\n", device, errno, strerror(errno));
close(serfd);
serfd = -1;
return false;
}
return true;
}
void CDVAPDongle::Stop()
{
dvapreg.header = 0x5u;
dvapreg.param.control = 0x18u;
dvapreg.param.byte = 0;
write_to_dvp(&dvapreg, 5);
return;
}
int CDVAPDongle::KeepAlive()
{
dvapreg.header = 0x6003u;
dvapreg.nul = 0x0u;
return write_to_dvp(&dvapreg, 3);
}
void CDVAPDongle::traceit(const char *fmt,...)
{
time_t ltime;
struct tm mytm;
const int TRACE_BFSZ = 256;
char trace_buf[TRACE_BFSZ];
time(&ltime);
localtime_r(&ltime,&mytm);
snprintf(trace_buf,TRACE_BFSZ - 1,"%02d/%02d/%02d %02d:%02d:%02d:",
mytm.tm_mon+1,mytm.tm_mday,mytm.tm_year % 100,
mytm.tm_hour,mytm.tm_min,mytm.tm_sec);
va_list args;
va_start(args,fmt);
vsnprintf(trace_buf + strlen(trace_buf), TRACE_BFSZ - strlen(trace_buf) - 1, fmt, args);
va_end(args);
fprintf(stdout, "%s", trace_buf);
return;
}

@ -0,0 +1,119 @@
#pragma once
/*
* Copyright 2017 by Thomas Early, AC2IE
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
enum REPLY_TYPE {
RT_TIMEOUT,
RT_ERR,
RT_UNKNOWN,
RT_NAME,
RT_SER,
RT_FW,
RT_START,
RT_STOP,
RT_MODU,
RT_MODE,
RT_SQL,
RT_PWR,
RT_OFF,
RT_FREQ,
RT_FREQ_LIMIT,
RT_STS,
RT_PTT,
RT_ACK,
RT_HDR,
RT_HDR_ACK,
RT_DAT
};
#pragma pack(push,1)
typedef struct dvp_register_tag {
uint16_t header;
union {
uint8_t nul;
struct {
uint16_t control;
union {
int8_t byte;
int16_t word;
int32_t dword;
int32_t twod[2];
char sstr[12];
uint8_t ustr[12];
};
} param;
struct {
uint16_t streamid;
uint8_t framepos;
uint8_t seq;
union {
struct {
unsigned char flag[3];
unsigned char rpt1[8];
unsigned char rpt2[8];
unsigned char urcall[8];
unsigned char mycall[8];
unsigned char sfx[4];
unsigned char pfcs[2];
} hdr;
struct {
unsigned char voice;
unsigned char sdata;
} vad;
};
} frame;
};
} SDVAP_REGISTER;
#pragma pack(pop)
class CDVAPDongle
{
public:
CDVAPDongle();
~CDVAPDongle();
bool Initialize(char *serialno, int frequency, int offset, int power, int squelch);
REPLY_TYPE get_reply(SDVAP_REGISTER &dr);
void Stop();
int KeepAlive();
void SendRegister(SDVAP_REGISTER &dr);
private:
// data
int serfd;
const unsigned int MAX_REPL_CNT;
uint32_t frequency;
int32_t offset;
SDVAP_REGISTER dvapreg;
// functions
bool OpenSerial(char *device);
int read_from_dvp(void* buf, unsigned int len);
int write_to_dvp(const void* buf, const unsigned int len);
void syncit();
bool get_ser(char *dvp, char *dvap_serial_number);
bool get_name();
bool get_fw();
bool set_modu();
bool set_mode();
bool set_sql(int squelch);
bool set_pwr(int power);
bool set_off(int offset);
bool set_freq(int frequency);
bool start_dvap();
void traceit(const char *fmt,...);
};

@ -35,8 +35,8 @@ g2_ircddb : g2_ircddb.cpp $(IRCDDBOBJS) versions.h
g2_link : g2_link.cpp versions.h g2_link : g2_link.cpp versions.h
g++ $(CPPFLAGS) -o g2_link g2_link.cpp -lrt -lconfig++ -pthread g++ $(CPPFLAGS) -o g2_link g2_link.cpp -lrt -lconfig++ -pthread
dvap_rptr : dvap_rptr.cpp dstar_dv.o golay23.o versions.h dvap_rptr : dvap_rptr.cpp DVAPDongle.o dstar_dv.o golay23.o DVAPDongle.h versions.h
g++ $(CPPFLAGS) -o dvap_rptr dvap_rptr.cpp golay23.o dstar_dv.o -I/usr/include -L/usr/lib -lrt -lconfig++ -pthread g++ $(CPPFLAGS) -o dvap_rptr dvap_rptr.cpp DVAPDongle.o golay23.o dstar_dv.o -I/usr/include -L/usr/lib -lrt -lconfig++ -pthread
dvrptr : dvrptr.cpp dstar_dv.o golay23.o dvrptr : dvrptr.cpp dstar_dv.o golay23.o
g++ $(CPPFLAGS) -o dvrptr dvrptr.cpp golay23.o dstar_dv.o -I/usr/include -L/usr/lib -lconfig++ -lrt g++ $(CPPFLAGS) -o dvrptr dvrptr.cpp golay23.o dstar_dv.o -I/usr/include -L/usr/lib -lconfig++ -lrt
@ -68,6 +68,9 @@ IRCDDBApp.o : IRCDDBApp.cpp IRCDDBApp.h IRCutils.h
aprs.o : aprs.cpp aprs.h aprs.o : aprs.cpp aprs.h
g++ -c $(CPPFLAGS) aprs.cpp g++ -c $(CPPFLAGS) aprs.cpp
DVAPDongle.o : DVAPDongle.cpp DVAPDongle.h
g++ -c $(CPPFLAGS) DVAPDongle.cpp
golay23.o : golay23.cpp golay23.h golay23.o : golay23.cpp golay23.h
g++ -c $(CPPFLAGS) golay23.cpp g++ -c $(CPPFLAGS) golay23.cpp

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save

Powered by TurnKey Linux.