Table of Contents

Protocole DPlus

:!: Page encore en cours d'édition :!:

Il n'y a pas de spécification publique du protocole DPlus.

Comme me l'a dit G4KLX: “None exists. The implementation is the specification.”

Je n'ai pas encore établi la parenté exacte du protocole, mais l'OM qui semble le plus calé est AA4RC. Il faut que j'embête un de ces 4 pour voir s'il a plus d'infos.

Ce que j'ai donc déduit pour l'instant:

Frame types

enum DPLUS_TYPE {
	DP_NONE,
	DP_HEADER,
	DP_AMBE,
	DP_POLL,
	DP_CONNECT
};

Frame type detection in OpenDV

bool CDPlusProtocolHandler::readPackets()
{
	m_type = DP_NONE;
 
	// No more data?
	int length = m_socket.read(m_buffer, BUFFER_LENGTH, m_yourAddress, m_yourPort);
	if (length <= 0)
		return false;
 
	m_length = length;
 
	if (m_buffer[2] != 'D' || m_buffer[3] != 'S' || m_buffer[4] != 'V' || m_buffer[5] != 'T') {
		switch (m_length) {
			case 3U:
				m_type = DP_POLL;
				return false;
			case 5U:
			case 8U:
			case 28U:
				m_type = DP_CONNECT;
				return false;
			default:
				// An unknown type
				// CUtils::dump(wxT("Unknown packet type from D-Plus"), m_buffer, m_length);
				return true;
		}
	} else {
		// Header or data packet type?
		if (m_buffer[0] == 0x3A && m_buffer[1] == 0x80) {
			m_type = DP_HEADER;
			return false;
		} else if (m_buffer[0] == 0x1D && m_buffer[1] == 0x80) {
			m_type = DP_AMBE;
			return false;
		} else if (m_buffer[0] == 0x20 && m_buffer[1] == 0x80) {
			m_type = DP_AMBE;
			return false;
		} else {
			// An unknown type
			CUtils::dump(wxT("Unknown packet type from D-Plus"), m_buffer, m_length);
			return true;
		}
	}
}

DV frame

Type: 0x80

Contient une trame de streaming D-Star au format de flux UDP utilisé en D-Star.

DV header

unsigned int CHeaderData::getDPlusData(unsigned char* data, unsigned int length, bool check) const
{
	wxASSERT(data != NULL);
	wxASSERT(length >= 58U);
 
	data[0]  = 0x3A;
	data[1]  = 0x80;
 
	data[2]  = 'D';
	data[3]  = 'S';
	data[4]  = 'V';
	data[5]  = 'T';
 
	data[6]  = 0x10;
	data[7]  = 0x00;
	data[8]  = 0x00;
	data[9]  = 0x00;
	data[10] = 0x20;
 
	data[11] = m_band1;
	data[12] = m_band2;
	data[13] = m_band3;
 
	data[14] = m_id % 256U;			// Unique session id
	data[15] = m_id / 256U;
 
	data[16] = 0x80;
 
	data[17] = 0x00;				// Flags 1, 2, and 3
	data[18] = 0x00;
	data[19] = 0x00;
 
	::memcpy(data + 20U, m_rptCall2, LONG_CALLSIGN_LENGTH);
	::memcpy(data + 28U, m_rptCall1, LONG_CALLSIGN_LENGTH);
	::memcpy(data + 36U, m_yourCall, LONG_CALLSIGN_LENGTH);
	::memcpy(data + 44U, m_myCall1,  LONG_CALLSIGN_LENGTH);
	::memcpy(data + 52U, m_myCall2,  SHORT_CALLSIGN_LENGTH);
 
	if (check) {
		CCCITTChecksum csum;
		csum.update(data + 17, 4U * LONG_CALLSIGN_LENGTH + SHORT_CALLSIGN_LENGTH + 3U);
		csum.result(data + 56);
	} else {
		data[56] = 0xFF;
		data[57] = 0xFF;
	}
 
	return 58U;
}

DV data (AMBE)

unsigned int CAMBEData::getDPlusData(unsigned char* data, unsigned int length) const
{
	wxASSERT(data != NULL);
	wxASSERT(length >= 32U);
 
	if (isEnd()) {
		data[0]  = 0x20;
		data[1]  = 0x80;
	} else {
		data[0]  = 0x1D;
		data[1]  = 0x80;
	}
 
	data[2]  = 'D';
	data[3]  = 'S';
	data[4]  = 'V';
	data[5]  = 'T';
 
	data[6]  = 0x20;
	data[7]  = 0x00;
	data[8]  = 0x00;
	data[9]  = 0x00;
	data[10] = 0x20;
 
	data[11] = m_band1;
	data[12] = m_band2;
	data[13] = m_band3;
 
	data[14] = m_id % 256U;			// Unique session id
	data[15] = m_id / 256U;
 
	data[16] = m_outSeq;
 
	if (isEnd()) {
		::memcpy(data + 17U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES);
		::memcpy(data + 26U, END_PATTERN_BYTES, END_PATTERN_LENGTH_BYTES);	// Add the end flag
		return 17U + DV_FRAME_MAX_LENGTH_BYTES;
	} else {
		// All other cases, just copy the payload
		::memcpy(data + 17U, m_data, DV_FRAME_LENGTH_BYTES);
		return 17U + DV_FRAME_LENGTH_BYTES;
	}
}

Connection notification

La commande est utilisée pour signaler son la connexion et la déconnexion d'un client au réflecteur.

Type: 0x00

05 00 24 00 XX

Valeur Nature de la commande
0 Connexion
1 Déconnexion

FIXME: trouver l'utilité des octets 2 et 3

Keepalive frame

Type: 0x60

{ 0x03, 0x60, 0x00 }

Command frame

La commande a l'air bien plus complète que ce qui est simplement présenté dans le code de G4KLX: dans dxrfd, on a plein d'autres données.

Type: 0xC0, soit 192.

Valeurs constatées pour l'octet 2:

Valeur Nature de la requete
3 Version
4 Login request
5 Connected user list
6 Linked repeaters
7 LH (ambiguous, need to dig that)
8 Local date

Keepalive frame

unsigned int CPollData::getDPlusData(unsigned char *data, unsigned int length) const
{
	wxASSERT(data != NULL);
	wxASSERT(length >= 3U);
 
	data[0U] = 0x03;
	data[1U] = 0x60;
	data[2U] = 0x00;
 
	return 3U;
}

Connection frame

Connection type

enum CD_TYPE {
	CT_LINK1,  // Connection
	CT_LINK2,  // Link request
	CT_UNLINK, // Disconnection
	CT_ACK,    // Link acknowledgement
	CT_NAK     // Link denial 
};
Response format

When the linking was successful, the server MUST send back the value “OKRW” in bytes 5-8.

If the link request wasn't satisfiable, the server can use any value which is not beginning by “OK”. Observed values are “FAIL” in dxrfd, “BUSY” in xlxd.

bool CConnectData::setDPlusData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
	wxASSERT(data != NULL);
	wxASSERT(length >= 5U);
	wxASSERT(yourPort > 0U);
 
	switch (length) {
		case 5U:
			switch (data[4U]) {
				case 0x01:
					m_type = CT_LINK1;
					break;
				case 0x00:
					m_type = CT_UNLINK;
					break;
			}
			break;
 
		case 8U: {
				wxString reply((const char*)(data + 4U), wxConvLocal, 4U);
				wxLogMessage(wxT("D-Plus reply is %.4s"), reply.c_str());
 
				if (::memcmp(data + 4U, "OKRW", 4U) == 0)
					m_type = CT_ACK;
				else
					m_type = CT_NAK;
			}
			break;
 
		case 28U:
			m_repeater = wxString((const char*)(data + 4U), wxConvLocal);
			m_type = CT_LINK2;
			break;
 
		default:
			return false;
	}
 
	m_yourAddress = yourAddress;
	m_yourPort    = yourPort;
	m_myPort      = myPort;
 
	return true;
}