I wrote a new version of my AM modulator that does quadrature. When you use a plain cosine wave (f(x) = cos(x) rather than f(x)=cos(2*pi*freq)), it makes a carrier that is almost at 0. Suppressed-carrier mirrored audio is always present in the center of my quadrature output, and it corrupts the desired signal if you don't modulate it onto a higher frequency than 0, as pictured.

You may be asking, why cosine? Because I saw that the waveform of a sine is 90 degrees

*ahead* of a cosine, and unless I'm mistaken the Q should come 90 degrees

*after* the I. Using I=sin and Q=cos, it was backwards. Yesterday this drove me crazy until I realized and corrected the mistake in the script.

It makes the difference of which side of 0 the signals end up on. Here's a picture of how it should look:

Something I noticed is that if you increase the frequency value inside the script then the output sin/cos waves will look distorted in Audacity. However, the audio demodulated by HDSDR sounds great either way, so it's not distorting much.

Wave when f=2

Wave when f=12

I also found an interesting relationship between frequency (the f in sin(2*pi*f)), the file's sampling rate, and the resulting signal center frequency.

The original audio file was 48 kHz and so was the quadrature output since the script copies the headers. When f=12, the AM signal's center frequency was about 10.1 kHz. When I fed the script a copy of the audio that was upsampled 4x to 192 kHz, the center frequency became 40.2 kHz. Changing f from 12 to 2 while keeping the 192 kHz rate made a precisely 6.7 kHz signal. In the case of 192 kHz that means (center_frequency / f) = 3.35.

In the case of 48 kHz, the constant is different. I generated a file with f=2 and a rate of 48 kHz to see the constant. In this scenario, the carrier wasn't on an easily readable frequency boundary, so I set RBW to 0.2Hz and zoomed in fully. I estimated it to be 1.675 kHz.

Well, it turns out I was only 8 Hz off. Rearranging the formula above: if constant = (center_frequency / f), then

centerfrequency = constant * f

With 48 kHz, the constant is 0.841667, so the center frequency is 1.683 kHz.

And one more relationship to tie it all together: notice how the constant for 192 kHz is close to 4x the constant for 48 kHz. So close, in fact, that we can approximate this:

constant(sampling_rate) = ~0.0175 * sampling_rate

where sampling_rate is kHz, not Hz. However, note that this is an approximation for estimation purposes and is NOT as accurate as the f versus center frequency constants.

Finally, here's the Python script used to generate everything illustrated here. It makes separate I and Q mono WAV files which you must put together as left and right stereo channels, respectively.

------------------

import math

def radians(degrees):

return (degrees/360)*2*math.pi

#plt.axis([0,1000,0,255])

#plt.ylabel('some numbers')

samples=1000000

x=[]

i=[]

q=[]

multiplier=[]

demod=[]

amp=1

freq=2 #1/8

phase=0

for d in range(samples):

x.append(d)

#y.append(amp*math.sin((2*math.pi*freq*radians(d))+phase))

sinwavevalue=math.sin(radians(2*math.pi*freq*d))

coswavevalue=math.cos(radians(2*math.pi*freq*d))

i.append((coswavevalue/2)+0.5)

q.append((sinwavevalue/2)+0.5)

#multiplier.append(amp*math.sin((2*math.pi*freq*radians(d))+phase))

#for element in range(len(y)):

#demod.append(y[element]*multiplier[element])

#plt.plot(x,demod)

#plt.plot(x[0:1000],y[0:1000])

#plt.show()

bytepos=0

with open("D:/time8a.wav","rb") as infile: #Input File

with open("D:/time_am_f2_i.wav","wb") as o: #Output file

#byte = i.read(1)

for idx in range(0,44):

byte=infile.read(1)

o.write(byte)

while byte:

bytepos +=1

if (bytepos == samples):

break

test=float(float(int.from_bytes(byte,byteorder="big"))*i[bytepos])

#test=float(127*i[bytepos])

#print(int.from_bytes(byte,byteorder="big")," * ",y[bytepos]," = ",float(float(int.from_bytes(byte,byteorder="big"))*y[bytepos]))

tmp = [int(test),]

#print(bytes(tmp)," (",int(test),")")

o.write(bytes(tmp))

byte = infile.read(1)

bytepos=0

with open("D:/time8a.wav","rb") as infile: #Input File

with open("D:/time_am_f2_q.wav","wb") as o: #Output file

#byte = i.read(1)

for idx in range(0,44):

byte=infile.read(1)

o.write(byte)

while byte:

bytepos +=1

if (bytepos == samples):

break

test=float(float(int.from_bytes(byte,byteorder="big"))*q[bytepos])

#test=float(127*q[bytepos])

#print(int.from_bytes(byte,byteorder="big")," * ",y[bytepos]," = ",float(float(int.from_bytes(byte,byteorder="big"))*y[bytepos]))

tmp = [int(test),]

#print(bytes(tmp)," (",int(test),")")

o.write(bytes(tmp))

byte = infile.read(1)