mirror of https://github.com/n7tae/tcd.git
parent
88f6aeba9e
commit
91f910ead0
@ -0,0 +1,221 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 by Jonathan Naylor G4KLX and John Hays K7VE
|
||||||
|
* Copyright 2016 by Jeremy McDermond (NH6Z)
|
||||||
|
* Copyright 2021 by Thomas Early N7TAE
|
||||||
|
*
|
||||||
|
* Adapted by K7VE from G4KLX dv3000d
|
||||||
|
*/
|
||||||
|
|
||||||
|
// tcd - a hybid transcoder using DVSI hardware and Codec2 software
|
||||||
|
// Copyright © 2022 Thomas A. Early N7TAE
|
||||||
|
|
||||||
|
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "DV3000.h"
|
||||||
|
#include "configure.h"
|
||||||
|
#include "Controller.h"
|
||||||
|
|
||||||
|
extern CController Controller;
|
||||||
|
|
||||||
|
CDV3000::CDV3000(Encoding t) : CDVDevice(t) {}
|
||||||
|
|
||||||
|
CDV3000::~CDV3000() {}
|
||||||
|
|
||||||
|
void CDV3000::FeedDevice()
|
||||||
|
{
|
||||||
|
const std::string modules(TRANSCODED_MODULES);
|
||||||
|
const auto n = modules.size();
|
||||||
|
while (keep_running)
|
||||||
|
{
|
||||||
|
auto packet = input_queue.pop(); // blocks until there is something to pop
|
||||||
|
|
||||||
|
|
||||||
|
while (keep_running) // wait until there is room
|
||||||
|
{
|
||||||
|
if (buffer_depth < 2)
|
||||||
|
break;
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keep_running)
|
||||||
|
{
|
||||||
|
auto index = modules.find(packet->GetModule());
|
||||||
|
// save the packet in the vocoder's queue while the vocoder does its magic
|
||||||
|
if (std::string::npos == index)
|
||||||
|
{
|
||||||
|
std::cerr << "Module '" << packet->GetModule() << "' is not configured on " << description << std::endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
waiting_packet.push(packet);
|
||||||
|
|
||||||
|
const bool needs_audio = (Encoding::dstar==type) ? packet->DStarIsSet() : packet->DMRIsSet();
|
||||||
|
|
||||||
|
if (needs_audio)
|
||||||
|
{
|
||||||
|
SendData(index, (Encoding::dstar==type) ? packet->GetDStarData() : packet->GetDMRData());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SendAudio(index, packet->GetAudioSamples());
|
||||||
|
}
|
||||||
|
buffer_depth++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDV3000::ReadDevice()
|
||||||
|
{
|
||||||
|
while (keep_running)
|
||||||
|
{
|
||||||
|
// wait for something to read...
|
||||||
|
DWORD RxBytes = 0;
|
||||||
|
while (0 == RxBytes)
|
||||||
|
{
|
||||||
|
EVENT_HANDLE eh;
|
||||||
|
pthread_mutex_init(&eh.eMutex, NULL);
|
||||||
|
pthread_cond_init(&eh.eCondVar, NULL);
|
||||||
|
DWORD EventMask = FT_EVENT_RXCHAR;
|
||||||
|
auto status = FT_SetEventNotification(ftHandle, EventMask, &eh);
|
||||||
|
if (FT_OK != status)
|
||||||
|
{
|
||||||
|
FTDI_Error("Setting Event Notification", status);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&eh.eMutex);
|
||||||
|
pthread_cond_wait(&eh.eCondVar, &eh.eMutex);
|
||||||
|
pthread_mutex_unlock(&eh.eMutex);
|
||||||
|
|
||||||
|
DWORD EventDWord, TxBytes, Status;
|
||||||
|
status = FT_GetStatus(ftHandle, &RxBytes, &TxBytes, &EventDWord);
|
||||||
|
if (FT_OK != status)
|
||||||
|
{
|
||||||
|
FTDI_Error("Getting Event Status", status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDV_Packet p;
|
||||||
|
if (! GetResponse(p))
|
||||||
|
{
|
||||||
|
unsigned int channel = p.field_id - PKT_CHANNEL0;
|
||||||
|
auto packet = waiting_packet.pop();
|
||||||
|
if (PKT_CHANNEL == p.header.packet_type)
|
||||||
|
{
|
||||||
|
if (12!=ntohs(p.header.payload_length) || PKT_CHAND!=p.payload.ambe.chand || 72!=p.payload.ambe.num_bits)
|
||||||
|
dump("Improper ambe packet:", &p, packet_size(p));
|
||||||
|
buffer_depth--;
|
||||||
|
if (Encoding::dstar == type)
|
||||||
|
packet->SetDStarData(p.payload.ambe.data);
|
||||||
|
else
|
||||||
|
packet->SetDMRData(p.payload.ambe.data);
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (PKT_SPEECH == p.header.packet_type)
|
||||||
|
{
|
||||||
|
if (323!=ntohs(p.header.payload_length) || PKT_SPEECHD!=p.payload.audio.speechd || 160!=p.payload.audio.num_samples)
|
||||||
|
dump("Improper audio packet:", &p, packet_size(p));
|
||||||
|
buffer_depth--;
|
||||||
|
packet->SetAudioSamples(p.payload.audio.samples, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dump("ReadDevice() ERROR: Read an unexpected device packet:", &p, packet_size(p));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (Encoding::dstar == type) // is this a DMR or a DStar device?
|
||||||
|
{
|
||||||
|
Controller.dstar_mux.lock();
|
||||||
|
Controller.RouteDstPacket(packet);
|
||||||
|
Controller.dstar_mux.unlock();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Controller.dmrst_mux.lock();
|
||||||
|
Controller.RouteDmrPacket(packet);
|
||||||
|
Controller.dmrst_mux.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDV3000::SendAudio(const uint8_t /*channel*/, const int16_t *audio) const
|
||||||
|
{
|
||||||
|
// Create Audio packet based on input int8_ts
|
||||||
|
SDV_Packet p;
|
||||||
|
p.start_byte = PKT_HEADER;
|
||||||
|
p.header.payload_length = htons(1 + sizeof(p.payload.audio3k));
|
||||||
|
p.header.packet_type = PKT_SPEECH;
|
||||||
|
p.field_id = PKT_SPEECHD;
|
||||||
|
p.payload.audio3k.num_samples = 160U;
|
||||||
|
for (int i=0; i<160; i++)
|
||||||
|
p.payload.audio3k.samples[i] = htons(audio[i]);
|
||||||
|
|
||||||
|
// send audio packet to DV3000
|
||||||
|
const DWORD size = packet_size(p);
|
||||||
|
DWORD written;
|
||||||
|
auto status = FT_Write(ftHandle, &p, size, &written);
|
||||||
|
if (FT_OK != status)
|
||||||
|
{
|
||||||
|
FTDI_Error("Error writing audio packet", status);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (size != written)
|
||||||
|
{
|
||||||
|
std::cerr << "Incomplete Speech Packet write on " << description << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDV3000::SendData(const uint8_t /* channel */, const uint8_t *data) const
|
||||||
|
{
|
||||||
|
// Create data packet
|
||||||
|
SDV_Packet p;
|
||||||
|
p.start_byte = PKT_HEADER;
|
||||||
|
p.header.payload_length = htons(1 + sizeof(p.payload.ambe3k));
|
||||||
|
p.header.packet_type = PKT_CHANNEL;
|
||||||
|
p.field_id = PKT_CHAND;
|
||||||
|
p.payload.ambe3k.num_bits = 72U;
|
||||||
|
memcpy(p.payload.ambe3k.data, data, 9);
|
||||||
|
|
||||||
|
// send data packet to DV3000
|
||||||
|
const DWORD size = packet_size(p);
|
||||||
|
DWORD written;
|
||||||
|
auto status = FT_Write(ftHandle, &p, size, &written);
|
||||||
|
if (FT_OK != status)
|
||||||
|
{
|
||||||
|
FTDI_Error("Error writing AMBE Packet", status);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (size != written)
|
||||||
|
{
|
||||||
|
std::cerr << "Incomplete AMBE Packet write on " << description << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// tcd - a hybid transcoder using DVSI hardware and Codec2 software
|
||||||
|
// Copyright © 2022 Thomas A. Early N7TAE
|
||||||
|
|
||||||
|
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#include "DVSIDevice.h"
|
||||||
|
|
||||||
|
class CDV3000 : public CDVDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CDV3000(Encoding t);
|
||||||
|
virtual ~CDV3000();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void FeedDevice();
|
||||||
|
void ReadDevice();
|
||||||
|
bool SendAudio(const uint8_t channel, const int16_t *audio) const;
|
||||||
|
bool SendData(const uint8_t channel, const uint8_t *data) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
CPacketQueue waiting_packet; // the packet currently being processed in each vocoder
|
||||||
|
};
|
||||||
@ -0,0 +1,223 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 by Jonathan Naylor G4KLX and John Hays K7VE
|
||||||
|
* Copyright 2016 by Jeremy McDermond (NH6Z)
|
||||||
|
* Copyright 2021 by Thomas Early N7TAE
|
||||||
|
*
|
||||||
|
* Adapted by K7VE from G4KLX dv3000d
|
||||||
|
*/
|
||||||
|
|
||||||
|
// tcd - a hybid transcoder using DVSI hardware and Codec2 software
|
||||||
|
// Copyright © 2022 Thomas A. Early N7TAE
|
||||||
|
|
||||||
|
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "DV3003.h"
|
||||||
|
#include "configure.h"
|
||||||
|
#include "Controller.h"
|
||||||
|
|
||||||
|
extern CController Controller;
|
||||||
|
|
||||||
|
CDV3003::CDV3003(Encoding t) : CDVDevice(t) {}
|
||||||
|
|
||||||
|
CDV3003::~CDV3003() {}
|
||||||
|
|
||||||
|
void CDV3003::FeedDevice()
|
||||||
|
{
|
||||||
|
const std::string modules(TRANSCODED_MODULES);
|
||||||
|
const auto n = modules.size();
|
||||||
|
while (keep_running)
|
||||||
|
{
|
||||||
|
auto packet = input_queue.pop(); // blocks until there is something to pop
|
||||||
|
|
||||||
|
|
||||||
|
while (keep_running) // wait until there is room
|
||||||
|
{
|
||||||
|
if (buffer_depth < 2)
|
||||||
|
break;
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keep_running)
|
||||||
|
{
|
||||||
|
auto index = modules.find(packet->GetModule());
|
||||||
|
// save the packet in the vocoder's queue while the vocoder does its magic
|
||||||
|
if (std::string::npos == index)
|
||||||
|
{
|
||||||
|
std::cerr << "Module '" << packet->GetModule() << "' is not configured on " << description << std::endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
waiting_packet[index].push(packet);
|
||||||
|
|
||||||
|
const bool needs_audio = (Encoding::dstar==type) ? packet->DStarIsSet() : packet->DMRIsSet();
|
||||||
|
|
||||||
|
if (needs_audio)
|
||||||
|
{
|
||||||
|
SendData(index, (Encoding::dstar==type) ? packet->GetDStarData() : packet->GetDMRData());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SendAudio(index, packet->GetAudioSamples());
|
||||||
|
}
|
||||||
|
buffer_depth++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDV3003::ReadDevice()
|
||||||
|
{
|
||||||
|
while (keep_running)
|
||||||
|
{
|
||||||
|
// wait for something to read...
|
||||||
|
DWORD RxBytes = 0;
|
||||||
|
while (0 == RxBytes)
|
||||||
|
{
|
||||||
|
EVENT_HANDLE eh;
|
||||||
|
pthread_mutex_init(&eh.eMutex, NULL);
|
||||||
|
pthread_cond_init(&eh.eCondVar, NULL);
|
||||||
|
DWORD EventMask = FT_EVENT_RXCHAR;
|
||||||
|
auto status = FT_SetEventNotification(ftHandle, EventMask, &eh);
|
||||||
|
if (FT_OK != status)
|
||||||
|
{
|
||||||
|
FTDI_Error("Setting Event Notification", status);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&eh.eMutex);
|
||||||
|
pthread_cond_wait(&eh.eCondVar, &eh.eMutex);
|
||||||
|
pthread_mutex_unlock(&eh.eMutex);
|
||||||
|
|
||||||
|
DWORD EventDWord, TxBytes, Status;
|
||||||
|
status = FT_GetStatus(ftHandle, &RxBytes, &TxBytes, &EventDWord);
|
||||||
|
if (FT_OK != status)
|
||||||
|
{
|
||||||
|
FTDI_Error("Getting Event Status", status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDV_Packet p;
|
||||||
|
if (! GetResponse(p))
|
||||||
|
{
|
||||||
|
unsigned int channel = p.field_id - PKT_CHANNEL0;
|
||||||
|
auto packet = waiting_packet[channel].pop();
|
||||||
|
if (PKT_CHANNEL == p.header.packet_type)
|
||||||
|
{
|
||||||
|
if (12!=ntohs(p.header.payload_length) || PKT_CHAND!=p.payload.ambe.chand || 72!=p.payload.ambe.num_bits)
|
||||||
|
dump("Improper ambe packet:", &p, packet_size(p));
|
||||||
|
buffer_depth--;
|
||||||
|
if (Encoding::dstar == type)
|
||||||
|
packet->SetDStarData(p.payload.ambe.data);
|
||||||
|
else
|
||||||
|
packet->SetDMRData(p.payload.ambe.data);
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (PKT_SPEECH == p.header.packet_type)
|
||||||
|
{
|
||||||
|
if (323!=ntohs(p.header.payload_length) || PKT_SPEECHD!=p.payload.audio.speechd || 160!=p.payload.audio.num_samples)
|
||||||
|
dump("Improper audio packet:", &p, packet_size(p));
|
||||||
|
buffer_depth--;
|
||||||
|
packet->SetAudioSamples(p.payload.audio.samples, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dump("ReadDevice() ERROR: Read an unexpected device packet:", &p, packet_size(p));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (Encoding::dstar == type) // is this a DMR or a DStar device?
|
||||||
|
{
|
||||||
|
Controller.dstar_mux.lock();
|
||||||
|
Controller.RouteDstPacket(packet);
|
||||||
|
Controller.dstar_mux.unlock();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Controller.dmrst_mux.lock();
|
||||||
|
Controller.RouteDmrPacket(packet);
|
||||||
|
Controller.dmrst_mux.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDV3003::SendAudio(const uint8_t channel, const int16_t *audio) const
|
||||||
|
{
|
||||||
|
// Create Audio packet based on input int8_ts
|
||||||
|
SDV_Packet p;
|
||||||
|
p.start_byte = PKT_HEADER;
|
||||||
|
p.header.payload_length = htons(1 + sizeof(p.payload.audio));
|
||||||
|
p.header.packet_type = PKT_SPEECH;
|
||||||
|
p.field_id = channel + PKT_CHANNEL0;
|
||||||
|
p.payload.audio.speechd = PKT_SPEECHD;
|
||||||
|
p.payload.audio.num_samples = 160U;
|
||||||
|
for (int i=0; i<160; i++)
|
||||||
|
p.payload.audio.samples[i] = htons(audio[i]);
|
||||||
|
|
||||||
|
// send audio packet to DV3000
|
||||||
|
const DWORD size = packet_size(p);
|
||||||
|
DWORD written;
|
||||||
|
auto status = FT_Write(ftHandle, &p, size, &written);
|
||||||
|
if (FT_OK != status)
|
||||||
|
{
|
||||||
|
FTDI_Error("Error writing audio packet", status);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (size != written)
|
||||||
|
{
|
||||||
|
std::cerr << "Incomplete Speech Packet write on " << description << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDV3003::SendData(const uint8_t channel, const uint8_t *data) const
|
||||||
|
{
|
||||||
|
// Create data packet
|
||||||
|
SDV_Packet p;
|
||||||
|
p.start_byte = PKT_HEADER;
|
||||||
|
p.header.payload_length = htons(1 + sizeof(p.payload.ambe));
|
||||||
|
p.header.packet_type = PKT_CHANNEL;
|
||||||
|
p.field_id = channel + PKT_CHANNEL0;
|
||||||
|
p.payload.ambe.chand = PKT_CHAND;
|
||||||
|
p.payload.ambe.num_bits = 72U;
|
||||||
|
memcpy(p.payload.ambe.data, data, 9);
|
||||||
|
|
||||||
|
// send data packet to DV3000
|
||||||
|
const DWORD size = packet_size(p);
|
||||||
|
DWORD written;
|
||||||
|
auto status = FT_Write(ftHandle, &p, size, &written);
|
||||||
|
if (FT_OK != status)
|
||||||
|
{
|
||||||
|
FTDI_Error("Error writing AMBE Packet", status);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (size != written)
|
||||||
|
{
|
||||||
|
std::cerr << "Incomplete AMBE Packet write on " << description << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// tcd - a hybid transcoder using DVSI hardware and Codec2 software
|
||||||
|
// Copyright © 2022 Thomas A. Early N7TAE
|
||||||
|
|
||||||
|
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#include "DVSIDevice.h"
|
||||||
|
|
||||||
|
class CDV3003 : public CDVDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CDV3003(Encoding t);
|
||||||
|
virtual ~CDV3003();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void FeedDevice();
|
||||||
|
void ReadDevice();
|
||||||
|
bool SendAudio(const uint8_t channel, const int16_t *audio) const;
|
||||||
|
bool SendData(const uint8_t channel, const uint8_t *data) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
CPacketQueue waiting_packet[3]; // the packet currently being processed in each vocoder
|
||||||
|
};
|
||||||
@ -0,0 +1,103 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// tcd - a hybid transcoder using DVSI hardware and Codec2 software
|
||||||
|
// Copyright © 2022 Thomas A. Early N7TAE
|
||||||
|
|
||||||
|
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#define USB3XXX_MAXPACKETSIZE 1024 // must be multiple of 64
|
||||||
|
|
||||||
|
#define PKT_HEADER 0x61
|
||||||
|
|
||||||
|
#define PKT_CONTROL 0x00
|
||||||
|
#define PKT_CHANNEL 0x01
|
||||||
|
#define PKT_SPEECH 0x02
|
||||||
|
|
||||||
|
#define PKT_SPEECHD 0x00
|
||||||
|
#define PKT_CHAND 0x01
|
||||||
|
#define PKT_RATET 0x09
|
||||||
|
#define PKT_INIT 0x0b
|
||||||
|
#define PKT_PRODID 0x30
|
||||||
|
#define PKT_VERSTRING 0x31
|
||||||
|
#define PKT_PARITYBYTE 0x2f
|
||||||
|
#define PKT_RESET 0x33
|
||||||
|
#define PKT_READY 0x39
|
||||||
|
#define PKT_CHANNEL0 0x40
|
||||||
|
#define PKT_CHANNEL1 0x41
|
||||||
|
#define PKT_CHANNEL2 0x42
|
||||||
|
#define PKT_PARITYMODE 0x3f
|
||||||
|
#define PKT_ECMODE 0x05
|
||||||
|
#define PKT_DCMODE 0x06
|
||||||
|
#define PKT_COMPAND 0x32
|
||||||
|
#define PKT_RATEP 0x0a
|
||||||
|
#define PKT_CHANFMT 0x15
|
||||||
|
#define PKT_SPCHFMT 0x16
|
||||||
|
#define PKT_GAIN 0x4b
|
||||||
|
|
||||||
|
#define packet_size(a) int(4 + ntohs((a).header.payload_length))
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
struct dv_packet_tag {
|
||||||
|
uint8_t start_byte;
|
||||||
|
struct {
|
||||||
|
uint16_t payload_length;
|
||||||
|
uint8_t packet_type;
|
||||||
|
} header;
|
||||||
|
uint8_t field_id; // for audio and ambe, this is channel# 0x40U, 0x41U or 0x42U
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
union {
|
||||||
|
char prodid[16];
|
||||||
|
uint8_t paritymode[3];
|
||||||
|
char version[48];
|
||||||
|
uint8_t resp[15]; // for the codec config response, ECMODE, DCMODE, RATEP, CHANFMT, SPCHFMT, GAIN and INIT
|
||||||
|
} data;
|
||||||
|
} ctrl;
|
||||||
|
struct {
|
||||||
|
uint8_t ecmode[3];
|
||||||
|
uint8_t dcmode[3];
|
||||||
|
uint8_t ratep[13];
|
||||||
|
uint8_t chfmt[3];
|
||||||
|
uint8_t spfmt[3];
|
||||||
|
uint8_t gain[3];
|
||||||
|
uint8_t init[2];
|
||||||
|
} codec;
|
||||||
|
struct {
|
||||||
|
uint8_t speechd; // 0
|
||||||
|
uint8_t num_samples; // 160
|
||||||
|
int16_t samples[160]; // 4 + 1 + 2 + 320 = 327 byte
|
||||||
|
} audio;
|
||||||
|
struct {
|
||||||
|
uint8_t chand; // 1
|
||||||
|
uint8_t num_bits; // 72 (9 bytes)
|
||||||
|
uint8_t data[9]; // 4 + 1 + 2 + 9 = 16 bytes
|
||||||
|
} ambe;
|
||||||
|
struct {
|
||||||
|
uint8_t num_samples; // 160
|
||||||
|
int16_t samples[160]; // 4 + 1 + 2 + 320 = 327 byte
|
||||||
|
} audio3k;
|
||||||
|
struct {
|
||||||
|
uint8_t num_bits; // 72 (9 bytes)
|
||||||
|
uint8_t data[9]; // 4 + 1 + 2 + 9 = 16 bytes
|
||||||
|
} ambe3k;
|
||||||
|
} payload;
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
using SDV_Packet = struct dv_packet_tag;
|
||||||
|
|
||||||
|
enum class Encoding { dstar, dmrsf };
|
||||||
|
enum class Edvtype { dv3000, dv3003 };
|
||||||
Loading…
Reference in new issue