Exemplo n.º 1
0
    def getAudio(self):
        '''Get the audio from data

        Returns:
            :obj:`commSignal`: An audio signal
        '''

        audioFreq = self.__audioFreq
        strictness = self.__strictness
        #print(audioFreq, self.__bw)

        audioOut = comm.commSignal(audioFreq)
        bhFilter = filters.blackmanHarris(151)
        fmDemdulator = demod_fm.demod_fm()
        chunkerObj = chunker.chunker(sigsrc)
        #print(chunkerObj.getChunks)
        #print(len(chunkerObj.getChunks[:10]))

        for i in chunkerObj.getChunks:
            offset = self.__offset

            sig = comm.commSignal(self.__sigsrc.sampFreq, self.__sigsrc.read(*i), chunkerObj)\
                .offsetFreq(self.__offset).filter(bhFilter)\
                .bwLim(self.__bw, uniq="First")\
                .funcApply(fmDemdulator.demod)\
                .bwLim(audioFreq, strictness)

            audioOut.extend(sig)

        return audioOut
Exemplo n.º 2
0
    def __audio(self, audioFreq=constants.NOAA_AUDSAMPRATE, strictness=True):
        '''Get the audio from data at this sampling rate

        Args:
            audioFreq (:obj:`int`, optional): Target frequency of sampling of audio
            strictness (:obj:`bool`, optional): Strictness of sampling

        Returns:
            :obj:`commSignal`: An audio signal
        '''

        logging.info('Beginning FM demodulation to get audio in chunks')

        audioOut = comm.commSignal(audioFreq)
        bhFilter = filters.blackmanHarris(151)
        fmDemdulator = demod_fm.demod_fm()
        chunkerObj = chunker.chunker(self.__sigsrc)

        for i in chunkerObj.getChunks:

            logging.info('Processing chunk %d of %d chunks',
                         chunkerObj.getChunks.index(i) + 1,
                         len(chunkerObj.getChunks))

            sig = comm.commSignal(
                self.__sigsrc.sampFreq, self.__sigsrc.read(*i),
                chunkerObj).offsetFreq(self.__offset).filter(bhFilter).bwLim(
                    self.__bw, uniq="First").funcApply(
                        fmDemdulator.demod).bwLim(audioFreq, strictness)
            audioOut.extend(sig)

        logging.info('FM demodulation successfully complete')
        self.__audOut = audioOut

        return audioOut
Exemplo n.º 3
0
# a commSignal object basically stores the signal array and its samplingrate
# if you want the array do sig.signal
# if you want the samping rate do sig.sampRate
sig = comm.commSignal(sigsrc.sampFreq, sigArray)

## Offset the frequency if required, not needed here
# sig.offsetFreq(0)

########### Apply a blackman harris filter to get rid of noise
bhFilter = filters.blackmanHarris(151)
sig.filter(bhFilter)

## Limit bandwidth, say 30000
sig.bwLim(30000)

## FM demodulate
fmDemodulator = demod_fm.demod_fm()
sig.funcApply(fmDemodulator.demod)

########### APRS has two freqs 1200 and 2200, hence create a butter band pass filter from 1200-1000 to 2200+1000
bFilter = filters.butter(sig.sampRate,
                         1200 - 1000,
                         2200 + 1000,
                         typeFlt=constants.FLT_BP)
sig.filter(bFilter)

## plot the signal
plt.plot(sig.signal)
plt.show()

#### We can clearly see that the filters provide a way better result
Exemplo n.º 4
0
    def getAccurateSync(self, useNormCorrelate=True):
        '''Get the sync locations: at highest sampling rate

        Args:
            useNormCorrelate (:obj:`bool`, optional): Whether to use normalized correlation or not

        Returns:
            :obj:`list`: A list of locations of sync in sample number (start of sync)
        '''

        if self.__asyncA is None or self.__asyncB is None or self.__asyncBtime is None or self.__asyncAtime is None or self.__asyncBpk is None or self.__asyncApk is None or not self.__useNormCorrelate == useNormCorrelate:
            self.__useNormCorrelate = useNormCorrelate

            if self.__syncA is None or self.__syncB is None:
                self.getCrudeSync()

            # calculate the width of search window in sample numbers
            syncTime = constants.NOAA_T * len(constants.NOAA_SYNCA)
            searchTimeWidth = 3 * syncTime
            searchSampleWidth = int(searchTimeWidth * self.__sigsrc.sampFreq)

            # convert sync from samples to time
            csyncA = self.__syncA / self.__syncCrudeSampRate
            csyncB = self.__syncB / self.__syncCrudeSampRate

            # convert back to sample number
            csyncA *= self.__sigsrc.sampFreq
            csyncB *= self.__sigsrc.sampFreq

            ## Accurate syncA
            self.__asyncA = []
            self.__asyncApk = []
            self.__asyncAtime = []
            logging.info('Beginning Accurate SyncA detection')

            for i in csyncA:

                logging.info('Detecting Sync %d of %d syncs',
                             list(csyncA).index(i) + 1, len(csyncA))

                startI = int(i) - int(searchSampleWidth)
                endI = int(i) + int(searchSampleWidth)
                if startI < 0 or endI > self.__sigsrc.length:
                    continue
                sig = comm.commSignal(
                    self.__sigsrc.sampFreq, self.__sigsrc.read(
                        startI, endI)).offsetFreq(self.__offset).filter(
                            filters.blackmanHarris(
                                151, zeroPhase=True)).funcApply(
                                    demod_fm.demod_fm().demod).funcApply(
                                        demod_am.demod_am().demod)
                syncDet, PkHeights, TimeSync = self.__correlateAndFindPeaks(
                    sig,
                    constants.NOAA_SYNCA,
                    getExtraInfo=True,
                    useNormCorrelate=useNormCorrelate,
                    usePosNeedle=useNormCorrelate,
                    useFilter=True)
                self.__asyncA.append(syncDet[0] + startI)
                self.__asyncApk.append(PkHeights[0])
                self.__asyncAtime.append(TimeSync[0])
            logging.info('Accurate SyncA detection complete')

            ## Accurate syncB
            self.__asyncB = []
            self.__asyncBpk = []
            self.__asyncBtime = []
            logging.info('Beginning Accurate SyncB detection')

            for i in csyncB:

                logging.info('Detecting Sync %d of %d syncs',
                             list(csyncB).index(i) + 1, len(csyncB))

                startI = int(i) - int(searchSampleWidth)
                endI = int(i) + int(searchSampleWidth)
                if startI < 0 or endI > self.__sigsrc.length:
                    continue
                sig = comm.commSignal(
                    self.__sigsrc.sampFreq, self.__sigsrc.read(
                        startI, endI)).offsetFreq(self.__offset).filter(
                            filters.blackmanHarris(
                                151, zeroPhase=True)).funcApply(
                                    demod_fm.demod_fm().demod).funcApply(
                                        demod_am.demod_am().demod)
                syncDet, PkHeights, TimeSync = self.__correlateAndFindPeaks(
                    sig,
                    constants.NOAA_SYNCB,
                    getExtraInfo=True,
                    useNormCorrelate=useNormCorrelate,
                    usePosNeedle=useNormCorrelate,
                    useFilter=True)
                self.__asyncB.append(syncDet[0] + startI)
                self.__asyncBpk.append(PkHeights[0])
                self.__asyncBtime.append(TimeSync[0])
            logging.info('Accurate SyncB detection complete')

        return [
            self.__asyncA,
            np.diff(self.__asyncA), self.__asyncApk, self.__asyncAtime,
            self.__asyncB,
            np.diff(self.__asyncB), self.__asyncBpk, self.__asyncBtime
        ]
Exemplo n.º 5
0
    def getMsg(self):
        '''Get the message from data

        Returns:
            :string: A string of message data
        '''

        if self.__msg is None:

            sig = comm.commSignal(self.__sigsrc.sampFreq)

            chunkerObj = chunker.chunker(self.__sigsrc)
            bhFilter = filters.blackmanHarris(151)
            fmDemodObj = demod_fm.demod_fm()

            for i in chunkerObj.getChunks:

                logging.info('Processing chunk %d of %d chunks',
                             chunkerObj.getChunks.index(i) + 1,
                             len(chunkerObj.getChunks))

                # get the signal
                chunkSig = comm.commSignal(self.__sigsrc.sampFreq,
                                           self.__sigsrc.read(*i), chunkerObj)

                ## Offset the frequency if required, not needed here
                chunkSig.offsetFreq(self.__offset)

                ## Apply a blackman harris filter to get rid of noise
                chunkSig.filter(bhFilter)

                ## Limit bandwidth
                chunkSig.bwLim(self.__bw)

                # store signal
                sig.extend(chunkSig)

            ## FM demodulate
            sig.funcApply(fmDemodObj.demod)
            logging.info('FM demod complete')

            ## APRS has two freqs 1200 and 2200, hence create a butter band pass filter from 1200-500 to 2200+500
            sig.filter(
                filters.butter(sig.sampRate,
                               1200 - 500,
                               2200 + 500,
                               typeFlt=constants.FLT_BP))
            logging.info('Filtering complete')

            ## plot the signal
            if self.__graphs == 1:
                plt.plot(sig.signal)
                plt.show()

            buffer_size = int(np.round(self.__bw / self.__BAUDRATE))
            SAMPLE_PER_BAUD = self.__bw // self.__BAUDRATE

            # creating the “correlation list" for the comparison frequencies of the digital frequency filers
            corr_mark_i = np.zeros(buffer_size)
            corr_mark_q = np.zeros(buffer_size)
            corr_space_i = np.zeros(buffer_size)
            corr_space_q = np.zeros(buffer_size)

            # filling the "correlation list" with sampled waveform for the two frequencies.
            for i in range(buffer_size):
                mark_angle = (i * 1.0 / self.__bw) / (
                    1 / self.__mark_frequency) * 2 * np.pi
                corr_mark_i[i] = np.cos(mark_angle)
                corr_mark_q[i] = np.sin(mark_angle)

                space_angle = (i * 1.0 / self.__bw) / (
                    1 / self.__space_frequency) * 2 * np.pi
                corr_space_i[i] = np.cos(space_angle)
                corr_space_q[i] = np.sin(space_angle)

            # now we check the full signal for the binary states, whether it is closer to 1200 hz or closer to 2200 Hz
            binary_filter = np.zeros(len(sig.signal))

            for sample in range(len(sig.signal) - buffer_size):
                corr_mi = 0
                corr_mq = 0
                corr_si = 0
                corr_sq = 0

                for sub in range(buffer_size):
                    corr_mi = corr_mi + sig.signal[sample +
                                                   sub] * corr_mark_i[sub]
                    corr_mq = corr_mq + sig.signal[sample +
                                                   sub] * corr_mark_q[sub]

                    corr_si = corr_si + sig.signal[sample +
                                                   sub] * corr_space_i[sub]
                    corr_sq = corr_sq + sig.signal[sample +
                                                   sub] * corr_space_q[sub]

                binary_filter[sample] = (corr_mi**2 + corr_mq**2 - corr_si**2 -
                                         corr_sq**2)
            logging.info('Binary filter complete')
            if self.__graphs == 1:
                plt.plot(sig.signal / np.max(sig.signal))
                plt.plot(np.sign(binary_filter))
                plt.show()

            # now trying to find the raising or falling edges of the bits
            # generating the edge detection kernel
            kernel = np.zeros(SAMPLE_PER_BAUD)
            for i in range(len(kernel)):
                if i < SAMPLE_PER_BAUD // 2:
                    kernel[i] = -1
                else:
                    kernel[i] = 1

            changes = np.correlate(np.sign(binary_filter), kernel,
                                   mode="same") / SAMPLE_PER_BAUD

            if self.__graphs == 1:
                plt.plot(np.sign(binary_filter))
                plt.plot(changes)
                plt.title("bit starts")
                plt.show()

            # by using the edges of the bits for synching the sampling to the transmitted bits, the algo is
            # self synchronizing.
            # but sometimes the crossing areas between the bits can be uncertain. for that, a peak detection defines
            # only one solution in close vicinity and defining the edges further.
            peaks = peakdetect.peakdetect(np.abs(changes),
                                          lookahead=int(SAMPLE_PER_BAUD *
                                                        0.65))

            peaks1_x = []
            peaks1_y = []

            # positive peaks
            for i in range(len(peaks[0])):
                peaks1_x.append(peaks[0][i][0])
                peaks1_y.append(peaks[0][i][1])

            if self.__graphs == 1:
                plt.plot(peaks1_x, peaks1_y, "o")
                plt.plot(np.abs(changes))
                plt.plot(np.sign(binary_filter))
                plt.plot(sig.signal / np.max(sig.signal))
                plt.show()

            bit_repeated = np.round(
                np.diff(peaks1_x) / (self.__bw / self.__BAUDRATE))
            logging.info('Bit repeat complete')
            if self.__graphs == 1:
                plt.plot(np.sign(binary_filter))
                plt.plot(peaks1_x[:-1], bit_repeated, "*")
                plt.grid()
                plt.title("where frequency shifts")
                plt.show()

            # making the bits for nrzi
            bitstream_nrzi = []

            c = 0
            for i in range(len(bit_repeated)):
                # print(c, i, x1[i], "p", int(bit_repeated[i]))
                for repeats in range(int(bit_repeated[i])):
                    bitstream_nrzi.append((np.mean(
                        binary_filter[peaks1_x[i] +
                                      repeats * SAMPLE_PER_BAUD:peaks1_x[i] +
                                      (repeats + 1) * SAMPLE_PER_BAUD])))
                    # print(c, bitstream_nrzi[-1])
                    c += 1

            # here we convert the nrzi bits to normal bits
            bitstream = decode_afsk1200.decode_nrzi(np.sign(bitstream_nrzi))
            logging.info('Decoding NRZI complete')

            if self.__graphs == 1:
                plt.plot(np.sign(bitstream_nrzi))
                plt.plot(bitstream_nrzi, "o-")
                plt.plot(bitstream, "*")
                plt.show()

            bit_startflag = []
            bit_startflag_marker = []

            for bit in range(len(bitstream) - 8):
                out = ""
                for i in range(8):
                    out += str(bitstream[bit + i])

                if out == "01111110":
                    # print(bit)
                    bit_startflag.append(bit)
                    bit_startflag_marker.append(1)

            length = np.diff(bit_startflag)

            # there are still the stuffed bits inside the bit stream, so we need to find them...
            bitstream_stuffed = decode_afsk1200.find_bit_stuffing(bitstream)

            if self.__graphs == 1:
                plt.plot(bitstream_nrzi)
                plt.plot(bitstream, "o-")
                plt.plot(bit_startflag[:-1], length, "o")
                plt.plot(bit_startflag, bit_startflag_marker, "o")
                plt.plot(bitstream_stuffed, "*")
                plt.title("test1")
                plt.show()
            logging.info('Stuffed bit removal complete')
            # checking at each possible start flag, if the bit stream was received correctly.
            # this is done by checking the crc16 at the end of the msg with the msg body.
            for flag in range(len(bit_startflag) - 1):

                # and firstly, we need to get rid of the stuffed bits, that are still inside the bit stream
                bits = decode_afsk1200.reduce_stuffed_bit(
                    bitstream[bit_startflag[flag] + 8:bit_startflag[flag + 1]],
                    bitstream_stuffed[bit_startflag[flag] +
                                      8:bit_startflag[flag + 1]])

                msg = bits[:-16]

                if len(bits) % 8 == 0 and len(msg) > 16 * 8:

                    out = ""
                    for i in range(len(msg)):
                        out += str(msg[i])
                    crc = framechecksequence.fcs_crc16(out)

                    crc_received = ""
                    msg_rest = bits[-16:]
                    for i in range(len(msg_rest)):
                        crc_received += str(msg_rest[i])

                    if crc_received == crc:
                        msg_text = decode_afsk1200.bits_to_msg(msg)

                        print("one aprs msg with correct crc is found. #",
                              flag, "starts at", bit_startflag[flag],
                              "length is",
                              len(bits) / 8)
                        msg_text

                        if self.__graphs == 1:
                            plt.plot(
                                bitstream[bit_startflag[flag] +
                                          8:bit_startflag[flag + 1] + 8], "o-")
                            plt.plot(bits, "*-")
                            plt.show()

                        # there can be several messages per stream, so for now only the last is stored.
                        # to-do
                        self.__msg = "template: space rocks!"
                        self.__useful = 1

            logging.info('Message extraction complete')

        return self.__msg