mayton2019
@mayton2019
Bigdata Engineer

Как программно декодировать 4-QAM сигнал в диапазоне звуковых частот?

Есть pet-project. Что-то вроде модема. Занимаюсь давно. Не спеша. И чисто из интереса к обработке сигналов.
Кодирую бинарный файл и получаю на выходе звуковой файл 4-QAM с несущей в 260 Гц в формате WAV.
Фазы соотв четыре штуки 45 градусов, 225, 315 e.t.c. Амплитуда берется по максимуму для 16 битного звука.
1 канал - моно. Длина импульса модулирующего сигнала ... пока еще не определена. Я буду искать ее
экспериментально чисто по результатам успешного декодирования.

Возникли вопросы с декодером. Как должен работать алгоритм пилот-тона? И как обеспечить синхронизацию
если во время декодирования фаза начнет "уползать" в сторону? Это будет неизбежно если я
файл буду передавать по голосовым каналам (радио, mp3).
  • Вопрос задан
  • 154 просмотра
Решения вопроса 1
Griboks
@Griboks
Как программно декодировать 4-QAM сигнал

QAM4 - можно рассматривать как QPSK, которую в свою очередь можно рассматривать как 2xBPSK, т. е. как два независимых сигнала Cos и Sin одинаковой частоты и амплитуды, но сдвинутых по фазе. Иными словами, решить систему уравнений вида sin+cos=A. В любом случае вы вычисляете точку на созвездии, а затем принимаете решение, какой именно из 4 фиксированных эта точка является.

Как должен работать алгоритм пилот-тона? И как обеспечить синхронизацию
если во время декодирования фаза начнет "уползать" в сторону?

Синхросигнал должен выполнять две функции:
1) Маркировать начало передачи или иным образом синхронизировать такты (фронт волны).
2) Детектировать условия среды передачи. Зная отправленный и имея принятый синхросигнал, можно определить параметры среды и определить корректирующие коэффициенты для дальнейшего приёма. В данном случае, это будет нулевая фаза и амплитуда.

Таким образом синхросигнал должен фиксировать все три параметра синусоид. Далее, принимая сигнал с данными, можно уже декодировать их.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 2
mayton2019
@mayton2019 Автор вопроса
Bigdata Engineer
QAM4 - можно рассматривать как QPSK, которую в свою очередь можно рассматривать как 2xBPSK, т. е. как два независимых сигнала Cos и Sin одинаковой частоты и амплитуды, но сдвинутых по фазе. Иными словами, решить систему уравнений вида sin+cos=A. В любом случае вы вычисляете точку на созвездии, а затем принимаете решение, какой именно из 4 фиксированных эта точка является.

Да. Я решил умножать измеряемый сигнал скалярно на синусоиду и косинусоиду. Для простоты Это будет
вектор кратный периоду семплов. Причем максимально большой чтоб не делать лишних расчетов.
И максимально маленький чтоб не превышать половину (или меньше) импульс модулирующего.
В результате получим набор звездочек. Они будут прыгать и плавать по комплексной плоскости и дальше
надо будет угадывать что есть что.

1) Маркировать начало передачи или иным образом синхронизировать такты (фронт волны).
2) Детектировать условия среды передачи. Зная отправленный и имея принятый синхросигнал, можно определить параметры среды и определить корректирующие коэффициенты для дальнейшего приёма. В данном случае, это будет нулевая фаза и амплитуда.

Я думал так. Пускай QAM фазы кодируют 00,01,10,11 биты соотвественно. Тогда пилот-тон будет длинной последовательностью (3 секунды) фазы 00 после которой любое изменение будет означать начало передачи.

Если в пилоте фаза 00 сместится с 45 градусов допустим на 130 - мне будет пофиг. Я просто буду считать ее
началом отсчета. Вычту из всех фаз 130. Ф1 = 130 градусов.

Далее. Если звезды будут дрейфовать или поворачиваться по кругу мне придется на ходу подстраивать
эту фазу Ф1. Вот как ее подстраивать я еще не придумал.

UPD: Не туда закинул. Модератор перекинь пожалуйста в комментарии.
Ответ написан
Комментировать
Gremlin92
@Gremlin92
Целеустремленный
Могу лишь предложить класс для загрузки и воспроизведения wav файла на с++ с библиотекой openal
Sound.h
//Sound.h
#pragma once
#include <AL/al.h>
#include <AL/alc.h>
#ifndef _WIN32
	#include <AL/alut.h>
#endif
#include <stdio.h>
#include <iostream>
#include <string>
#ifdef _WIN32
	#include <io.h>
	#include <Windows.h>
#else
	#include <unistd.h>
	#include <fcntl.h>
	#include <sys/stat.h>
#endif // _WIN32
#ifdef _WIN32
struct WAVHEADER
{
	// WAV-ôîðìàò íà÷èíàåòñÿ ñ RIFF-çàãîëîâêà:
	// Ñîäåðæèò ñèìâîëû "RIFF" â ASCII êîäèðîâêå
	// (0x52494646 â big-endian ïðåäñòàâëåíèè)
	char chunkId[4];
	// 36 + subchunk2Size, èëè áîëåå òî÷íî:
	// 4 + (8 + subchunk1Size) + (8 + subchunk2Size)
	// Ýòî îñòàâøèéñÿ ðàçìåð öåïî÷êè, íà÷èíàÿ ñ ýòîé ïîçèöèè.
	// Èíà÷å ãîâîðÿ, ýòî ðàçìåð ôàéëà - 8, òî åñòü,
	// èñêëþ÷åíû ïîëÿ chunkId è chunkSize.
	unsigned long chunkSize;
	// Ñîäåðæèò ñèìâîëû "WAVE"
	// (0x57415645 â big-endian ïðåäñòàâëåíèè)
	char format[4];
	// Ôîðìàò "WAVE" ñîñòîèò èç äâóõ ïîäöåïî÷åê: "fmt " è "data":
	// Ïîäöåïî÷êà "fmt " îïèñûâàåò ôîðìàò çâóêîâûõ äàííûõ:
	// Ñîäåðæèò ñèìâîëû "fmt "
	// (0x666d7420 â big-endian ïðåäñòàâëåíèè)
	char subchunk1Id[4];
	// 16 äëÿ ôîðìàòà PCM.
	// Ýòî îñòàâøèéñÿ ðàçìåð ïîäöåïî÷êè, íà÷èíàÿ ñ ýòîé ïîçèöèè.
	unsigned long subchunk1Size;
	// Àóäèî ôîðìàò, ïîëíûé ñïèñîê ìîæíî ïîëó÷èòü çäåñü http://audiocoding.ru/wav_formats.txt
	// Äëÿ PCM = 1 (òî åñòü, Ëèíåéíîå êâàíòîâàíèå).
	// Çíà÷åíèÿ, îòëè÷àþùèåñÿ îò 1, îáîçíà÷àþò íåêîòîðûé ôîðìàò ñæàòèÿ.
	unsigned short audioFormat;
	// Êîëè÷åñòâî êàíàëîâ. Ìîíî = 1, Ñòåðåî = 2 è ò.ä.
	unsigned short numChannels;
	// ×àñòîòà äèñêðåòèçàöèè. 8000 Ãö, 44100 Ãö è ò.ä.
	unsigned long sampleRate;
	// sampleRate * numChannels * bitsPerSample/8
	unsigned long byteRate;
	// numChannels * bitsPerSample/8
	// Êîëè÷åñòâî áàéò äëÿ îäíîãî ñýìïëà, âêëþ÷àÿ âñå êàíàëû.
	unsigned short blockAlign;
	// Òàê íàçûâàåìàÿ "ãëóáèíàÿ" èëè òî÷íîñòü çâó÷àíèÿ. 8 áèò, 16 áèò è ò.ä.
	unsigned short bitsPerSample;
	// Ïîäöåïî÷êà "data" ñîäåðæèò àóäèî-äàííûå è èõ ðàçìåð.
	// Ñîäåðæèò ñèìâîëû "data"
	// (0x64617461 â big-endian ïðåäñòàâëåíèè)
	char subchunk2Id[4];
	// numSamples * numChannels * bitsPerSample/8
	// Êîëè÷åñòâî áàéò â îáëàñòè äàííûõ.
	unsigned long subchunk2Size;
	// Äàëåå ñëåäóþò íåïîñðåäñòâåííî Wav äàííûå.
};
#endif // _WIN32
struct Sounds
{
	unsigned int* buffer;
	unsigned int* source;
	std::string* Name;
	int* number;
};
class Sound
{
public:
	Sound();
	int Init(int argc, char*argv[]);
	int Play(int src);
	int Stop(int src, bool force);
	int StopAll();
	void SetPlay(int num);
	void Pause(int num);
	virtual ~Sound();
private:
protected:
	ALCdevice* dev;
	ALCcontext* ctx;
	int state;
	unsigned char* buf;
	unsigned int size_, freq;
	int format;
	int file;
	int org;
	Sounds *sounds;
	int CounterSounds, CountSounds;
};

Sound.cpp
//Sound.cpp
#include "Sound.h"
#ifdef _WIN32
	#pragma warning(disable : 4996)
	#pragma comment(lib,"OpenAL32.lib")
#endif

Sound::Sound()
{
	CountSounds = 1;
	sounds = new Sounds[CountSounds];
	sounds->Name = new std::string[CountSounds];
	for (int i = 0; i < CountSounds; i++)
		sounds->Name[i] = "";
	sounds->buffer = new unsigned int[CountSounds];
	sounds->source = new unsigned int[CountSounds];
	sounds->number = new int[CountSounds];
	CounterSounds = 0;
}
int Sound::Init(int argc, char*argv[])
{
#ifndef _WIN32
    alutInit(&argc, argv);
#endif
	dev = alcOpenDevice(0);
	ctx = alcCreateContext(dev, 0);
	alcMakeContextCurrent(ctx);
	alGenBuffers(CountSounds, sounds->buffer);
	alGenSources(CountSounds, sounds->source);
	sounds->Name[0] = "4-qam.wav";
	for (int j = 0; j < CountSounds; j++)
	{
#ifdef _WIN32
		FILE* file_;
		errno_t err;
		err = fopen_s(&file_,sounds->Name[j].c_str(), "rb");
		if (err)
		{
			printf_s("Failed open file, error %d", err);
			return 0;
		}
		WAVHEADER header;
		fread_s(&header, sizeof(WAVHEADER), sizeof(WAVHEADER), 1, file_);
		/*
		printf("chunkId:%s\n", header.chunkId);
		printf("chunkSize:%lu\n", header.chunkSize);
		printf("format:%s\n", header.format);
		printf("subchunk1Id:%s\n", header.subchunk1Id);
		printf("subchunk1Size:%lu\n", header.subchunk1Size);
		printf("audioformat:%d\n", header.audioFormat);
		printf("numCannels:%d\n", header.numChannels);
		printf("sampleRate:%lu\n", header.sampleRate);
		printf("byteRate:%lu\n", header.byteRate);
		printf("blockAlign:%d\n", header.blockAlign);
		printf("bitsPerSample:%d\n", header.bitsPerSample);
		printf("subchunk2Id:%s\n", header.subchunk2Id);
		printf("subchunk2Size:%lu\n", header.subchunk2Size);
		*/
		fclose(file_);
		if (header.numChannels == 1)
		{
			if (header.subchunk1Size == 16)
				format = AL_FORMAT_MONO16;
			else
				format = AL_FORMAT_MONO8;
		}
		else
		{
			if (header.subchunk1Size == 16)
				format = AL_FORMAT_STEREO16;
			else
				format = AL_FORMAT_STEREO8;
		}
		file = open(sounds->Name[j].c_str(), _A_SYSTEM);
		if (file == -1)
		{
			printf("Open failed on input file: %s\n",sounds->Name[j].c_str());
			wchar_t path[MAX_PATH];
			GetCurrentDirectory(sizeof(path), path);
			std::wcout << path << std::endl;
		}
		org = 0;
		lseek(file,0,org);
		//std::cout<<"hchs="<<header.subchunk2Size<<std::endl;
		buf = new unsigned char[header.subchunk2Size];
		read(file, buf, header.subchunk2Size);
		close(file);
		alBufferData(sounds->buffer[j], format, buf, header.subchunk2Size, header.sampleRate);
		delete buf;
#else
        sounds->buffer[j] = alutCreateBufferFromFile(sounds->Name[j].c_str());
#endif // _WIN32
		alSourcei(sounds->source[j], AL_BUFFER, sounds->buffer[j]);
	}

	return 0;
}
int Sound::Play(int src)
{
	alGetSourcei(sounds->source[src], AL_SOURCE_STATE, &state);
	if (state == AL_PLAYING)
		return 0;
	alSourcePlay(sounds->source[src]);
	return 1;
}
int Sound::Stop(int src, bool force)
{
	alGetSourcei(sounds->source[src], AL_SOURCE_STATE, &state);
	if (state == AL_PLAYING || force)
		alSourceStop(sounds->source[src]);
	return 0;
}
int Sound::StopAll()
{
	for (int i = 0; i < CountSounds; i++)
		Stop(i, false);
	return 0;
}

void Sound::Pause(int src)
{
	alGetSourcei(sounds->source[src], AL_SOURCE_STATE, &state);
	if (state == AL_PLAYING)
		alSourcePause(sounds->source[src]);
}
Sound::~Sound()
{
	alDeleteSources(CountSounds, sounds->source);
	alDeleteBuffers(CountSounds, sounds->buffer);
	delete sounds;
	delete buf;
	alcMakeContextCurrent(0);
	alcDestroyContext(ctx);
	alcCloseDevice(dev);
}

main.cpp
#include "Sound.h"
void main(int argc,char*argv[])
{
        Sound*Sound_ = new Sound();
	Sound_->Init(argc, argv);
        Sound_->Play(0);
}

Надо ковырять buf - там вся инфа и заголовок header
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы